Почему .СЕТЕВЫЕ консольные программы не заходят в тупик так, как это делают программы пользовательского интерфейса?

#.net #asynchronous

#.net #асинхронный

Вопрос:

Программы пользовательского интерфейса (WPF или Windows Forms) и ASP.NET программы должны использовать async и await тщательно использовать, чтобы избежать взаимоблокировки. Почему консольная программа не имеет такого же требования? Например, в следующем блокировка on await в MyGetAsync, а также блокировка at .Result в MyInvoker не приводит к взаимоблокировке.

 static void Main(string[] args)
{
    Console.WriteLine("test...");
    MyInvoker();
    Console.WriteLine("END. Press the Enter key to exit the program.");
    Console.ReadLine();
}
internal static void MyInvoker()
{
    Task<string> task = MyGetAsync();
    string s = task.Resu<
    if (s.Contains("doctype") || s.Contains("DOCTYPE"))
        Console.WriteLine("received an HTML page");
    else
        throw new Exception(s);
}
internal static async Task<string> MyGetAsync()
{
    using (var httpClient = new HttpClient())
    {
        string response = await httpClient.GetStringAsync("https://google.com");
        return response;
    }
}
 

Ответ №1:

Классическая тупиковая ситуация состоит из двух частей:

  1. Блокировка асинхронного кода (который используется не ConfigureAwait(false) везде).
  2. Асинхронный контекст, который одновременно разрешает только один поток.

Ваш пример кода всегда выполняет (1) в обеих средах. Разница в (2). В частности, приложения пользовательского интерфейса имеют a SynchronizationContext для своего потока пользовательского интерфейса, поэтому они await естественным образом возвращаются в поток пользовательского интерфейса после await завершения. Консольные приложения не имеют такого контекста, поэтому тупиковой ситуации не происходит.

Комментарии:

1. «Контекст» расплывчатый. «Поток», кажется, легче представить как поток управления. В примере кода поток управления передается в «вызывается» при ожидании. Вскоре после этого «вызывающий» блокирует поток управления. Когда «вызываемый» в конечном итоге продолжается, кажется, что у него нет потока для продолжения, потому что «вызывающий» заблокировал поток.

2. @H2ONaCl: «контекст» подробно описан в связанных сообщениях в блоге. Это так SynchronizationContext.Current , если это null не так, и в этом случае это так TaskScheduler.Current .

3. Также: не думайте о потоках, когда находитесь в async мире; это может ввести в заблуждение. В случае пользовательского интерфейса, да, SynchronizationContext привязан к определенному потоку пользовательского интерфейса. Однако такая же тупиковая ситуация может возникнуть на ASP.NET предварительное ядро, где SynchronizationContext оно не привязано к определенному потоку, но по-прежнему допускает только один поток за раз .

4. Отсутствие определенного контекста мало чем отличается от утверждения, что у него нет проблемы взаимоблокировки, что уже очевидно. Я мог бы возразить: «если не WPF, то используйте magic для работы, несмотря на кажущийся заблокированный метод, ожидающий заблокированного метода».

5. По первой ссылке раздел «что вызывает тупик» кажется более длинным описанием очевидной причины считать, что тупик возможен. Просто более подробное описание того, что я описал здесь. Загадка в том, почему внешний вид обманчив в случае консольных приложений.

Ответ №2:

Вы по-разному взаимодействуете с пользовательским интерфейсом, таким как Windows Forms и WPF, по сравнению с консольным приложением. Обычно вы ждете, пока консольное приложение не скажет вам что-то сделать, прежде чем делать, а затем ждете, пока это не будет сделано. В результате нет необходимости беспокоиться о том, что логика потоков конкурирует с ресурсами пользовательского интерфейса. С Windows Forms и WPF (на самом деле с любым графическим интерфейсом) у вас происходит много событий, о которых вы можете не знать, и людям трудно помнить, что бизнес-логика не входит в поток пользовательского интерфейса, иначе это действительно не было бы большой проблемой. Теперь, много раз, люди делают это, когда они уже написали огромную часть своей программы, прежде чем она окончательно заблокировалась, и в этот момент им приходится пересматривать довольно много, чтобы правильно ее обработать..