问题
I'm reading hundreds of answers about await
/async
, but there is still no answer to this simple question.
There is an awaitable method, written by someone else and it is necessary to wait till it finishes.
// cannot be changed
public async Task ThirdPartyAsync(...)
{
// calls other awaitable methods
// takes few seconds
}
// I'd like something like this
public void MyPatientCallingMethod(...)
{
// 1. call the method
ThirdPartyAsync(...);
// 2. wait for ThirdPartyAsync to finish (I don't care how long)
// 3. return after
return;
}
Yes, I know, it will block the main thread, the UI thread, any other thread... It has to be solved this way for safety reasons.
The problem is the return value of the async
method is Task
, and there is no .Result
tag to wait for.
Is there any other deadlock-free solution?
回答1:
You can use Task.Wait() to block on the task.
// call the method and wait for ThirdPartyAsync to finish (I don't care how long)
ThirdPartyAsync(...).Wait();
回答2:
That's because the best answer is "don't". You should really strive to be async all the way.
I can't use await because I have to be sure the method is finished before I do anything else.
Note that async
is serial (meaning the method won't continue past the await
until the inner method completes); it's just asynchronously serial instead of synchronously serial. So the same programming constructs and logic apply just the same:
public async Task MyPatientCallingMethodAsync(...)
{
// 1. call the method
var task = ThirdPartyAsync(...);
// 2. wait for ThirdPartyAsync to finish
await task;
// 3. return after
return;
}
This is exactly the approach you should take the vast, vast majority of the time.
However, if you positively, absolutely cannot do this, then your options are limited. Stephen Toub describes your options on his blog.
It is important to note that none of the options work in all scenarios and that every option has drawbacks and pitfalls. So none of us can say which one is best, or even which one would work since we don't know in what context your code is executing.
In summary, your options are:
- Synchronously block using
GetAwaiter().GetResult()
orWait
(see @l3arnon's answer for the difference). E.g.ThirdPartyAsync(...).GetAwaiter().GetResult();
. The drawback to this approach is that deadlocks are a real possibility if called in a UI or ASP.NET context. - Offload to a thread pool thread and block on that. E.g.,
Task.Run(() => ThirdPartyAsync(...)).GetAwaiter().GetResult();
. The drawback to this approach is that the third-party code must run on an alternate thread, so this won't work for code assuming a UI or ASP.NET context. - Execute a nested message loop. E.g.,
AsyncContext.Run(() => ThirdPartyAsync(...));
. The drawbacks to this approach are that the nested loop may not do everything you need (i.e., it may not pump Win32 messages, which the third-party code may require), the nested context does not match expected contexts (i.e., this can cause ASP.NET calls to fail), and the nested loop may do too much (i.e., it may pump Win32 messages, which can cause unexpected reentrancy).
In short, although it sounds simple enough, "sync over async" is one of the most complicated things you can (try to) do. And this is the long explanation behind the common answer of "just use async all the way!" :)
回答3:
Any Task
without a generic parameter lacks Result because it is effectively a void
return - there is no Result
to wait for.
Instead, what you're looking for is Task.Wait()
(as mentioned in other answers).
回答4:
From the comments it is now apparent what the misunderstanding is: You assume that await
starts a new thread and continues execution. The opposite is the case: await
pauses execution until its argument is finished. The Task
-returning method you are calling is supposed to initiate asynchrony. await
consumes that.
Use await
. It's now the standard way to perform long-running work without blocking the UI.
回答5:
You can use:
Wait()
- Would throwAggregateException
if the task is faulted.GetAwaiter().GetResult()
- Would throw the first exception (asawait
would do).- Stephen Cleary's AsyncContext:
public void MyPatientCallingMethod(...)
{
AsyncContext.Run(() => ThirdPartyAsync(...));
}
The
AsyncContext
type provides a context for executing asynchronous operations. Theawait
keyword requires a context to return back to; for most programs, this is a UI context, and you don't have to worry about it. ASP.NET also provides a proper context and you don't have to use your own.
However, Console applications and Win32 services do not have a suitable context, andAsyncContext
could be used in those situations.
来源:https://stackoverflow.com/questions/27869403/calling-a-3rd-party-async-task-method-synchronously