1

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?

David
  • 10,458
  • 1
  • 28
  • 40
Janos
  • 165
  • 10
  • have you used any synchronization objects or using a lock? – Daniel A. White Jan 09 '15 at 21:20
  • 3
    Why don't you use await? This seems to be intentional. – usr Jan 09 '15 at 21:26
  • 1
    Synchronously waiting for the task, as you mentioned, is just going to result in a deadlock. You need to not synchronously wait in order to not deadlock. – Servy Jan 09 '15 at 21:29
  • I didn't use locks. Not clear how it affects the execution order in this case. – Janos Jan 09 '15 at 21:33
  • I can't use await because I have to be sure the method is finished before I do anything else. – Janos Jan 09 '15 at 21:35
  • 2
    @JanosAdorjan That's what await does.. in an async method – i3arnon Jan 09 '15 at 21:35
  • @JanosAdorjan It sounds like you should simply be synchronizing access to some shared resource then, to prevent code that shouldn't be running in parallel to this operation from running. Blocking the UI thread is a pretty bad way of doing that. – Servy Jan 09 '15 at 21:37

5 Answers5

4

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();
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
3

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!" :)

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

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

David
  • 10,458
  • 1
  • 28
  • 40
2

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.

controlflow
  • 6,667
  • 1
  • 31
  • 55
i3arnon
  • 113,022
  • 33
  • 324
  • 344
2

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.

usr
  • 168,620
  • 35
  • 240
  • 369