Странное поведение, когда асинхронная программа не работает асинхронно - PullRequest
0 голосов
/ 15 апреля 2020

Я напечатал, какой поток выполняет каждый вызванный метод в следующей программе, и результаты очень странные. Я ожидал, что все асинхронные вызовы будут выполняться потоком потоков, потому что @Jon Skeet упомянул, что асинхронные вызовы используют потоки ThreadPool, а потоки пула потоков являются фоновыми потоками.

Идентификатор потока показывает, что тот же поток, который выполняет метод Main, выполняет все вызываемые методы, кроме DisplayResult, который не является асинхронным методом.

Как получается, что каждый вызов DisplayResult, который не является асинхронным методом, выполняется потоком потоков, тогда как действительные асинхронные методы при вызове выполняются не фоновым потоком, тем же не фоновым потоком что выполнил метод Main? А сколько фоновых и не фоновых потоков должно быть в этой программе?

public static async Task Main()
        {
            Console.WriteLine($"Main method: The thread executing this task is {Thread.CurrentThread.Name}, {Thread.CurrentThread.ManagedThreadId}");

            if (Thread.CurrentThread.IsBackground)
            {
                Console.WriteLine($"And it is a background thread.");

            }
            else
            {
                Console.WriteLine("And it is a non-background thread");
            }

            Console.WriteLine();

            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Console.WriteLine("It is a ThreadPoolThread");
            }
            else
            {
                Console.WriteLine("It is a non-ThreadPoolThread");
            }

            Task task1 = ProcessReadWriteAsync(@"/tmp/temp1Write.txt");

            Task task2 = ProcessReadWriteAsync(@"/tmp/temp2Write.txt");

            await task1;
            await task2;
        }

        public static async Task ProcessReadWriteAsync(string filePath)
        {
            Console.WriteLine($"ProcessReadWriteAsync: The thread executing this task is {Thread.CurrentThread.Name}, {Thread.CurrentThread.ManagedThreadId}");
            if (Thread.CurrentThread.IsBackground)
            {
                Console.WriteLine($"And it is a background thread.");

            }
            else
            {
                Console.WriteLine("And it is a non-background thread");
            }

            Console.WriteLine();

            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Console.WriteLine("It is a ThreadPoolThread");
            }
            else
            {
                Console.WriteLine("It is a non-ThreadPoolThread");
            }

            try
            {
            await ReadWriteAsync(filePath);

            } catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            } finally
            {
                Console.WriteLine();
            }
        }

        public static async Task ReadWriteAsync(string path, string text)
        {
            Console.WriteLine($"ReadWriteAsync 2 parameters: The thread executing this task is {Thread.CurrentThread.Name}, {Thread.CurrentThread.ManagedThreadId}");

            if (Thread.CurrentThread.IsBackground)
            {
                Console.WriteLine($"And it is a background thread.");

            }
            else
            {
                Console.WriteLine("And it is a non-background thread");
            }

            Console.WriteLine();

            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Console.WriteLine("It is a ThreadPoolThread");
            }
            else
            {
                Console.WriteLine("It is a non-ThreadPoolThread");
            }

            FileStream stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, useAsync: true);

                byte[] buffer = new byte[0x1000];

                int noOfCharactersRead = await stream.ReadAsync(buffer, 0, buffer.Length);

                DisplayResult(buffer: buffer);
        }

        public static async Task ReadWriteAsync(string path)
        {
            Console.WriteLine($"ReadWriteAsync 1 parameters: The thread executing this task is {Thread.CurrentThread.Name}, {Thread.CurrentThread.ManagedThreadId}");
            if (Thread.CurrentThread.IsBackground)
            {
                Console.WriteLine($"And it is a background thread.");

            }
            else
            {
                Console.WriteLine("And it is a non-background thread");
            }

            Console.WriteLine();

            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Console.WriteLine("It is a ThreadPoolThread");
            }
            else
            {
                Console.WriteLine("It is a non-ThreadPoolThread");
            }

            await ReadWriteAsync(path, "");
        }

        private static void DisplayResult(byte[] buffer)
        {
            Console.WriteLine($"DisplayResult: The thread executing this method is {Thread.CurrentThread.Name}, {Thread.CurrentThread.ManagedThreadId}");
            if (Thread.CurrentThread.IsBackground)
            {
                Console.WriteLine($"And it is a background thread.");

            }
            else
            {
                Console.WriteLine("And it is a non-background thread");
            }

            Console.WriteLine();

            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Console.WriteLine("It is a ThreadPoolThread");
            }
            else
            {
                Console.WriteLine("It is a non-ThreadPoolThread");
            }

            string DecodedText = Encoding.UTF8.GetString(buffer, 0, buffer.Length);

            string[] strings = DecodedText.Split('\n');

            for (int index = 0; index < strings.Length;index++)
            {
                Console.WriteLine(strings[index]);
            }
        }

Вывод:

Main method: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ProcessReadWriteAsync: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ReadWriteAsync 1 parameters: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ReadWriteAsync 2 parameters: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ProcessReadWriteAsync: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ReadWriteAsync 1 parameters: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ReadWriteAsync 2 parameters: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
DisplayResult: The thread executing this method is , 4
And it is a background thread.

It is a ThreadPoolThread
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                     42972
8. msdn.microsoft.com/library/ff730837.aspx               146159
9. msdn.microsoft.com/library/hh191443.aspx                83732
10. msdn.microsoft.com/library/aa578028.aspx               205273
11. msdn.microsoft.com/library/jj155761.aspx                29019
12. msdn.microsoft.com/library/hh290140.aspx               117152
13. msdn.microsoft.com/library/hh524395.aspx                68959
14. msdn.microsoft.com/library/ms404677.aspx               197325
15. msdn.microsoft.com                                     42972
16. msdn.microsoft.com/library/ff730837.aspx               146159
17. msdn.microsoft.com/library/hh191443.aspx                83732
18. msdn.microsoft.com/library/aa578028.aspx               205273
19. msdn.microsoft.com/library/jj155761.aspx                29019
20. msdn.microsoft.com/library/hh290140.aspx               117152
21. msdn.microsoft.com/library/hh524395.aspx                68959
22. msdn.microsoft.com/library/ms404677.aspx               197325
23. msdn.microsoft.com                                     42972
24. msdn.microsoft.com/library/ff730837.aspx               146159
25. msdn.microsoft.com/library/hh191443.aspx                83732
26. msdn.microsoft.com/library/aa578028.aspx               205273
27. msdn.microsoft.com/library/jj155761.aspx                29019
28. msdn.microsoft.com/library/hh290140.aspx               117152
29. msdn.microsoft.com/library/hh524395.aspx                68959
30. msdn.microsoft.com/library/ms404677.aspx               197325
31. msdn.microsoft.com                                     42972
32. msdn.microsoft.com/library/ff730837.aspx               146159
33. msdn.microsoft.com/library/hh191443.aspx                83732
34. msdn.microsoft.com/library/aa578028.aspx               205273
35. msdn.microsoft.com/library/jj155761.aspx                29019
36. msdn.microsoft.com/library/hh290140.aspx               117152
37. msdn.microsoft.com/library/hh524395.aspx                68959
38. msdn.microsoft.com/library/ms404677.aspx               197325
39. msdn.microsoft.com                                     42972
40. msdn.microsoft.com/library/ff730837.aspx               146159
41. msdn.microsoft.com/library/hh191443.aspx                83732
42. msdn.microsoft.com/library/aa578028.aspx               205273
43. msdn.microsoft.com/library/jj155761.aspx                29019
44. msdn.microsoft.com/library/hh290140.aspx               117152
45. msdn.microsoft.com/library/hh524395.aspx                68959
46. msdn.microsoft.com/library/ms404677.aspx               197325
47. msdn.microsoft.com                                     42972
48. msdn.microsoft.com/library/ff730837.aspx               146159
49. msdn.microsoft.com/library/hh191443.aspx                83732
50. msdn.microsoft.com/library/aa578028.aspx               205273
51. msdn.microsoft.com/library/jj155761.aspx                29019
52. msdn.microsoft.com/library/hh290140.aspx               117152
53. msdn.microsoft.com/library/hh524395.aspx                68959
54. msdn.microsoft.com/library/ms404677.aspx               197325
55. msdn.microsoft.com                                     42972
56. msdn.microsoft.com/library/ff730837.aspx               146159
57. msdn.microsoft.com/library/hh191443.aspx                83732
58. msdn.microsoft.com/library/aa578028.aspx               205273
59. msdn.microsoft.com/library/jj155761.aspx                29019
60. msdn.microsoft.com/library/hh290140.aspx               117152
61. msdn.microsoft.com/library/hh524395.aspx                68959
62. msdn.microsoft.com/library/ms404677.aspx               197325
63. msdn.microsoft.c

DisplayResult: The thread executing this method is , 5
And it is a background thread.

It is a ThreadPoolThread
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                     42972
8. msdn.microsoft.com/library/ff730837.aspx               146159
9. msdn.microsoft.com/library/hh191443.aspx                83732
10. msdn.microsoft.com/library/aa578028.aspx               205273
11. msdn.microsoft.com/library/jj155761.aspx                29019
12. msdn.microsoft.com/library/hh290140.aspx               117152
13. msdn.microsoft.com/library/hh524395.aspx                68959
14. msdn.microsoft.com/library/ms404677.aspx               197325
15. msdn.microsoft.com                                     42972
16. msdn.microsoft.com/library/ff730837.aspx               146159
17. msdn.microsoft.com/library/hh191443.aspx                83732
18. msdn.microsoft.com/library/aa578028.aspx               205273
19. msdn.microsoft.com/library/jj155761.aspx                29019
20. msdn.microsoft.com/library/hh290140.aspx               117152
21. msdn.microsoft.com/library/hh524395.aspx                68959
22. msdn.microsoft.com/library/ms404677.aspx               197325
23. msdn.microsoft.com                                     42972
24. msdn.microsoft.com/library/ff730837.aspx               146159
25. msdn.microsoft.com/library/hh191443.aspx                83732
26. msdn.microsoft.com/library/aa578028.aspx               205273
27. msdn.microsoft.com/library/jj155761.aspx                29019
28. msdn.microsoft.com/library/hh290140.aspx               117152
29. msdn.microsoft.com/library/hh524395.aspx                68959
30. msdn.microsoft.com/library/ms404677.aspx               197325
31. msdn.microsoft.com                                     42972
32. msdn.microsoft.com/library/ff730837.aspx               146159
33. msdn.microsoft.com/library/hh191443.aspx                83732
34. msdn.microsoft.com/library/aa578028.aspx               205273
35. msdn.microsoft.com/library/jj155761.aspx                29019
36. msdn.microsoft.com/library/hh290140.aspx               117152
37. msdn.microsoft.com/library/hh524395.aspx                68959
38. msdn.microsoft.com/library/ms404677.aspx               197325
39. msdn.microsoft.com                                     42972
40. msdn.microsoft.com/library/ff730837.aspx               146159
41. msdn.microsoft.com/library/hh191443.aspx                83732
42. msdn.microsoft.com/library/aa578028.aspx               205273
43. msdn.microsoft.com/library/jj155761.aspx                29019
44. msdn.microsoft.com/library/hh290140.aspx               117152
45. msdn.microsoft.com/library/hh524395.aspx                68959
46. msdn.microsoft.com/library/ms404677.aspx               197325
47. msdn.microsoft.com                                     42972
48. msdn.microsoft.com/library/ff730837.aspx               146159
49. msdn.microsoft.com/library/hh191443.aspx                83732
50. msdn.microsoft.com/library/aa578028.aspx               205273
51. msdn.microsoft.com/library/jj155761.aspx                29019
52. msdn.microsoft.com/library/hh290140.aspx               117152
53. msdn.microsoft.com/library/hh524395.aspx                68959
54. msdn.microsoft.com/library/ms404677.aspx               197325
55. msdn.microsoft.com                                     42972
56. msdn.microsoft.com/library/ff730837.aspx               146159
57. msdn.microsoft.com/library/hh191443.aspx                83732
58. msdn.microsoft.com/library/aa578028.aspx               205273
59. msdn.microsoft.com/library/jj155761.aspx                29019
60. msdn.microsoft.com/library/hh290140.aspx               117152
61. msdn.microsoft.com/library/hh524395.aspx                68959
62. msdn.microsoft.com/library/ms404677.aspx               197325
63. msdn.microsoft.c

1 Ответ

3 голосов
/ 15 апреля 2020

Все асинхронные методы запускаются в текущем потоке. Ничего другого не происходит, пока вы не нажмете await, который действует на неполный Task. Но что бы вы ни подавали на await, оно должно действительно возвращать значение (Task), прежде чем await действительно что-либо сделает.

Итак, давайте точно рассмотрим, что происходит:

  1. Main работает до тех пор, пока не вызовет ProcessReadWriteAsync(@"/tmp/temp1Write.txt")
  2. ProcessReadWriteAsync(string) работает до тех пор, пока не вызовет ReadWriteAsync(filePath)
  3. ReadWriteAsync(string), пока не вызовет ReadWriteAsync(path, "")
  4. ReadWriteAsync(string,string) работает до тех пор, пока не вызовет stream.ReadAsync(buffer, 0, buffer.Length)
  5. stream.ReadAsync() делает волхвы c, пока не вернет неполное Task
  6. await в ReadWriteAsync(string,string), см. неполный Task и возвращает новый неполный Task, а остальная часть метода подписана как продолжение этого Task
  7. ReadWriteAsync(string) возвращает неполное Task
  8. ProcessReadWriteAsync(string) возвращает неполное Task
  9. В Main() еще нет await, поэтому выполнение продолжается там.
  10. Main() вызывает ProcessReadWriteAsync(@"/tmp/temp2Write.txt"), который снова запускает весь этот процесс.

Все это происходит в одном и том же потоке.

Когда вы наконец вызываете await task1, он говорит ему приостановить выполнение n, пока Task не будет завершено. На данный момент ваш код больше не выполняется.

Когда stream.ReadAsync() наконец завершается, его Task устанавливается на Completed, а остальная часть ReadWriteAsync(string,string) выполняется до завершения, что затем вызывает ReadWriteAsync(string) для выполнения до завершения, который затем запускает ProcessReadWriteAsync(string) для выполнения до завершения. Все это происходит в фоновом потоке, поэтому вы видите DisplayResult, работающий в фоновом потоке.

После того, как task1 настроен на завершение, ваш метод Main() возобновляется.

Имейте в виду, что в приложении с контекстом синхронизации, таком как приложение пользовательского интерфейса или ASP. NET (не Core), продолжения не происходят в фоновом потоке. Они будут происходить в том же потоке, с которого они начали. Так что в этих случаях продолжения не начнутся, пока вы не нажмете await task1. Однако вы можете сказать, что они не нужны для возврата в тот же контекст синхронизации с .ConfigureAwait(false).

...