Wait () вызывает зависание потока пользовательского интерфейса - когда следует использовать Wait ()?


У меня есть следующий код, который подключается к узлу SignalR

    private static async Task StartListening()
    {
        try
        {


            var hubConnection = new HubConnection("http://localhost:8080/");                
            IHubProxy hubProxy = hubConnection.CreateHubProxy("Broadcaster");
            hubProxy.On<EventData>("notifyCardAccessEvent", eventData =>
            {
                Log.Info(string.Format("Incoming data: {0} {1}", eventData.Id, eventData.DateTime));
            });
            ServicePointManager.DefaultConnectionLimit = 10;
            await hubConnection.Start();
            Log.Info("Connected");
        }
        catch (Exception ex)
        {
            Log.Error(ex);
        }
    }

В моем методе Form_Load у меня есть этот

StartListening();

Однако Resharper предлагает мне "рассмотреть возможность применения оператора 'await' к результату вызова "

Так я и сделал:

Log.Info("Connecting to SignalR hub...");
StartListening().Wait();
Log.Info("Connected!");

Однако это приводит к зависанию потока пользовательского интерфейса и Connected! никогда не печатается в файл журнала.

Итак, мой вопрос в том, когда я должен использовать Wait()? Каковы примеры и сценарии, которые я должен использовать Wait (), и когда следует Я не использую Wait()?
3 2

3 ответа:

await не является Wait. Неясно, что это за код, который вызывает StartListening(), но один из вариантов-это await, как было предложено:

await StartListening();

Однако, в некоторых других случаях это может быть лучше вообще ничего не делать:

StartListening(); // drop the Task on the floor

Или, возможно, использовать ContinueWith для ручного продолжения. Поскольку метод StartListening ловит любые исключения, нет ничего плохого в том, чтобы просто игнорировать возвращенный Task - так что у вас уже было. Хотя я бы предложил назвать его StartListeningAsync.

Причина возникновения взаимоблокировка заключается в том, что если вы используете Wait, ваш поток пользовательского интерфейса блокирует ожидание завершения асинхронного метода, но этот асинхронный метод захватывает контекст синхронизации, что означает, что для обработки каждого продолжения он пытается попасть в поток пользовательского интерфейса, который блокируется... на нем .

@MarcGravell имеет правильный ответ; я просто отвечу на этот другой вопрос:

Итак, мой вопрос заключается в том, когда я должен использовать Wait()? Каковы примеры и сценарии, которые я должен использовать Wait (), и когда я не должен использовать Wait()?

Путаница возникает из-за того, что тип Task используется для двух почти совершенно разных вещей.

Task первоначально был представлен в .NET 4.0 как часть параллельной библиотеки задач. Обычно, ты будет использовать параллельный LINQ или класс Parallel для параллельной обработки (который использовал тип Task внизу). Однако в расширенных сценариях можно использовать тип Task непосредственно. Task.Wait использовался для ожидания завершения этих независимых задач.

Когда async/await были введены в .NET 4.5, существующий тип Task был почти достаточно хорош, чтобы использоваться в качестве абстрактного "будущего". Поэтому вместо того, чтобы изобретать какой-то новый тип" будущего", они просто немного расширили Task, чтобы работать как будущее.

Это приводит нас к сегодняшнему дню, где Task может использоваться как:

  • элемент работы в параллельном вычислении.
  • асинхронное будущее.

(есть крошечный кроссворд: вы можете рассматривать параллельную работу как асинхронную, и в редких ситуациях, таких как консольные методы Main, Вам нужно блокировать асинхронные задачи; но на данный момент игнорируйте их.)

Это означает, что API для Task разбивается по этим линиям. Члены такие как Start, Wait, Result, и ContinueWith довольно прочно принадлежат параллельной стороне. В асинхронном мире await более уместно.

У меня есть маленький столик в нижней части моего async введение , которое имеет некоторые новые (асинхронные) эквиваленты для старых (параллельных) способов выполнения вещей.

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

Этот приятный ответ даст больше информации о различиях: https://stackoverflow.com/a/13140963/3465395