Pergunta

I'm not sure if this is a SignalR issue or an async/await issue. When my client app (WPF) starts up, it does some initialisation:-

public async void Initialise()
{
    // Get data from the server - async, as it may be long-running.
    var data = await _hubProxy.Invoke<FooData>("Method1");

    _dataProcessor.ProcessData(data);
}

The _dataProcessor is a helper class that does some stuff with the data passed to it, then at some point calls a different server method using a line similar to:-

var moreData = _hubProxy.Invoke<BarData>("Method2").Result;

None of the code in the helper class is async.

The first server invoke (in Initialise()) works fine - it gets back the data and passes it to the helper class. This proceeds without issue until its invoke - it gets called but never returns, and the thread never proceeds past this line. I've put a breakpoint in the server method and can confirm that it is being called and returning a value, but for some reason this is not getting back to the client.

The strange thing is, if I take out the async/await keywords in the Initialise() method, everything works fine. What am I doing wrong? Why would these keywords affect the synchronous code in the helper class?

(I realise you shouldn't normally use async void, but as the Initialise() method is "fire and forget", I thought it was okay in this scenario).

Foi útil?

Solução

This line causes a deadlock on the UI thread:

var moreData = _hubProxy.Invoke<BarData>("Method2").Result;

Make ProcessData an async method and await _hubProxy.Invoke inside it:

var moreData = await _hubProxy.Invoke<BarData>("Method2");

Then, await _dataProcessor.ProcessData in your Initialise:

await _dataProcessor.ProcessData(data);

Make sure you don't use .Result or .Wait anywhere else.

Another way of solving it is just this:

public async void Initialise()
{
    // Get data from the server - async, as it may be long-running.
    var data = await _hubProxy.Invoke<FooData>("Method1").ConfigureAwait(false);

    _dataProcessor.ProcessData(data);
}

Note ConfigureAwait(false). In this case the continuation after await will happen on a non-UI thread. This will eliminate the deadlock, but this is not an ideal solution, rather a workaround. Moreover, your logic may require the continuation on the UI thread, for UI access or some thread-safety concerns.

The best solution would be to use both ConfigureAwait(false) (if possible) and avoid blocking with .Result or .Wait, at the same time.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top