Calling a 3rd party async Task method synchronously

不想你离开。 提交于 2020-01-15 12:15:40

问题


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() or Wait (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:

  1. Wait() - Would throw AggregateException if the task is faulted.
  2. GetAwaiter().GetResult() - Would throw the first exception (as await would do).
  3. Stephen Cleary's AsyncContext:

public void MyPatientCallingMethod(...)
{
    AsyncContext.Run(() => ThirdPartyAsync(...));
}

The AsyncContext type provides a context for executing asynchronous operations. The await 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, and AsyncContext could be used in those situations.



来源:https://stackoverflow.com/questions/27869403/calling-a-3rd-party-async-task-method-synchronously

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!