问题
I recently came across this code written by a contractor we had working for us. It's either devilishly clever or silly (I think the latter but I wanted a second opinion). I'm not massively up to speed on async
await
.
Basically it worked like this:
public bool Send(TemplatedMessageDto message)
{
return Task.Run(() => SendAsync(message))
.GetAwaiter()
.GetResult();
}
public async Task<bool> SendAsync(TemplatedMessageDto message)
{
//code doing stuff
var results = await _externalresource.DothingsExternally();
//code doing stuff
}
Now as I understand it that first Task.Run()
is pointless and inefficient? and should really be:
public bool Send(TemplatedMessageDto message)
{
return SendAsync(message))
.GetAwaiter()
.GetResult();
}
public async Task<bool> SendAsync(TemplatedMessageDto message)
{
//code doing stuff
var results = await _externalresource.DothingsExternally();
//code doing stuff
}
I'm also not convinced this is really an async method because it will still wait, right? I think it's only advantage (even re-written) is to free up the main worker thread.
Can someone confirm that this first Task shouldn't be there?
回答1:
I'm also not convinced this is really an async method because it will still wait, right?
It isn't, as Yuval explained. You shouldn't use sync over async.
Now as I understand it that first
Task.Run()
is pointless and inefficient?
Not really, there is value in using Task.Run
in such a way.
Since you're blocking on an async method (which you shouldn't do) there is a chance you'll deadlock. That happens in UI apps and asp.net where you have a SynchronizationContext
.
Using Task.Run
clears that SynchronizationContext
since it offloads the work to a ThreadPool
thread and removes the risk for a deadlock.
So, blocking is bad, but if you end up doing it using Task.Run
is safer.
回答2:
I'm also not convinced this is really an async method because it will still wait, right?
What your contractor did is use the sync over async anti-pattern. He probably did so to save himself from creating an additional method which does its work synchronously. He is unnecessarly invoking Task.Run
and immediately blocking on it using GetResult
.
Using GetAwaiter().GetResult()
will propogate the inner exception, if such happens, instead of the wrapped AggregateException
.
I think it's only advantage (even re-written) is to free up the main worker thread.
Both your version and his will block the main thread while executing, while his will also do so by using a threadpool thread. As Bar mentioned, this can help avoid deadlocks with issues regarding to synchronization context marshaling. I would recommend creating a synchronous equivalent if needed.
来源:https://stackoverflow.com/questions/32606485/calling-an-async-method-using-a-task-run-seems-wrong