Wait() causes UI thread to hang - when should Wait

2019-07-20 03:36发布

问题:

I have the following code that connects to a SignalR Hub

    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);
        }
    }

In my Form_Load method, I have this

StartListening();

However, Resharper prompts me to "consider applying the 'await' operator to the result of the call"

So I did this:

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

However, this causes my UI thread to hang and Connected! is never printed to the log file.

So my question is, when should I use Wait()? What are the instances and scenarios that I should use Wait(), and when should I not use Wait()?

回答1:

await is not Wait. It is unclear what the code is that is calling StartListening(), but one option is to await it, as suggested:

await StartListening();

However, in some other cases it may be better to do nothing at all:

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

or perhaps use ContinueWith for a manual continuation. Since the StartListening method catches any exceptions, there isn't anything wrong with just ignoring the returned Task - so what you had already. I would suggest calling it StartListeningAsync, though.

The reason for the deadlock is that if you use Wait, your UI thread blocks waiting on an asynchronous method to complete, but that asynchronous method is capturing the sync-context, which means in order to process each continuation it tries to get onto the UI thread - which is blocked... on it.



回答2:

@MarcGravell has the correct answer; I'm just going to answer this other question:

So my question is, when should I use Wait()? What are the instances and scenarios that I should use Wait(), and when should I not use Wait()?

The confusion is coming from the fact that the Task type is used for two almost completely different things.

Task was originally introduced in .NET 4.0 as part of the Task Parallel Library. Normally, you would use Parallel LINQ or the Parallel class for parallel processing (which used the Task type underneath). However, in advanced scenarios, you could use the Task type directly. Task.Wait was used to wait for those independent tasks to complete.

When async/await were introduced in .NET 4.5, the existing Task type was almost good enough to be used as an abstract "future". So instead of inventing some new "future" type, they just slightly extended Task to work as a future.

This brings us to today, where Task can be used as either:

  • An item of work in a parallel computation.
  • An asynchronous future.

(There's a tiny bit of crossover: you can treat parallel work as asynchronous, and in rare situations like Console Main methods you do need to block on asynchronous tasks; but ignore those for the moment.)

This means that the API for Task is split along those lines. Members such as Start, Wait, Result, and ContinueWith belong pretty firmly on the parallel side. In the asynchronous world, await is more appropriate.

I have a small table at the bottom of my async intro that has some new (asynchronous) equivalents for the old (parallel) ways of doing things.



回答3:

You seem to misunderstand the message from Resharper. Instead of applying the await operator, you called the Task.Wait() Method. They may seem similar, but they're working completely different.

This nice answer will provide more information about the differences: https://stackoverflow.com/a/13140963/3465395