2

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()?

Null Reference
  • 11,260
  • 40
  • 107
  • 184

3 Answers3

5

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 2
    @the8thbit far from; `Wait` is if I ask you to do something, and then I stand right behind you and do nothing else until you've finished it. `await` is if I ask you to do something, and then come and tap me on the shoulder when you've finished and when I'm not in the middle of something. Kind of. Analogies suck ;p The "sync context" in this is *sort of* "how to get my attention" - but that is stretching things a bit ;p But to illustrate: one option is to tell it *not to* capture the sync-context - in *that* case, when you've finished, **you** would go and do whatever it is I wanted done next – Marc Gravell Mar 28 '14 at 08:10
  • 1
    @the8thbit or, depending on the exact scenario, you'd leave a "go do this next" in the in-tray of the worker pool – Marc Gravell Mar 28 '14 at 08:13
4

@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.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
1

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

Community
  • 1
  • 1
Sigma Bear
  • 931
  • 5
  • 7