Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Asynchronous programming does "grow" through the code base. It has been <a href="https://blogs.msdn.microsoft.com/lucian/2011/04/15/async-ctp-refresh-design-changes/" rel="noreferrer">compared to a zombie virus</a>. The best solution is to allow it to grow, but sometimes that's not possible.</p> <p>I have written a few types in my <a href="https://github.com/StephenCleary/AsyncEx" rel="noreferrer">Nito.AsyncEx</a> library for dealing with a partially-asynchronous code base. There's no solution that works in every situation, though.</p> <p><strong>Solution A</strong></p> <p>If you have a simple asynchronous method that doesn't need to synchronize back to its context, then you can use <code>Task.WaitAndUnwrapException</code>:</p> <pre><code>var task = MyAsyncMethod(); var result = task.WaitAndUnwrapException(); </code></pre> <p>You do <em>not</em> want to use <code>Task.Wait</code> or <code>Task.Result</code> because they wrap exceptions in <code>AggregateException</code>.</p> <p>This solution is only appropriate if <code>MyAsyncMethod</code> does not synchronize back to its context. In other words, every <code>await</code> in <code>MyAsyncMethod</code> should end with <code>ConfigureAwait(false)</code>. This means it can't update any UI elements or access the ASP.NET request context.</p> <p><strong>Solution B</strong></p> <p>If <code>MyAsyncMethod</code> does need to synchronize back to its context, then you may be able to use <code>AsyncContext.RunTask</code> to provide a nested context:</p> <pre><code>var result = AsyncContext.RunTask(MyAsyncMethod).Result; </code></pre> <hr> <p>*Update 4/14/2014: In more recent versions of the library the API is as follows:</p> <pre><code>var result = AsyncContext.Run(MyAsyncMethod); </code></pre> <hr> <p>(It's OK to use <code>Task.Result</code> in this example because <code>RunTask</code> will propagate <code>Task</code> exceptions).</p> <p>The reason you may need <code>AsyncContext.RunTask</code> instead of <code>Task.WaitAndUnwrapException</code> is because of a rather subtle deadlock possibility that happens on WinForms/WPF/SL/ASP.NET:</p> <ol> <li>A synchronous method calls an async method, obtaining a <code>Task</code>.</li> <li>The synchronous method does a blocking wait on the <code>Task</code>.</li> <li>The <code>async</code> method uses <code>await</code> without <code>ConfigureAwait</code>.</li> <li>The <code>Task</code> cannot complete in this situation because it only completes when the <code>async</code> method is finished; the <code>async</code> method cannot complete because it is attempting to schedule its continuation to the <code>SynchronizationContext</code>, and WinForms/WPF/SL/ASP.NET will not allow the continuation to run because the synchronous method is already running in that context.</li> </ol> <p>This is one reason why it's a good idea to use <code>ConfigureAwait(false)</code> within every <code>async</code> method as much as possible.</p> <p><strong>Solution C</strong></p> <p><code>AsyncContext.RunTask</code> won't work in every scenario. For example, if the <code>async</code> method awaits something that requires a UI event to complete, then you'll deadlock even with the nested context. In that case, you could start the <code>async</code> method on the thread pool:</p> <pre><code>var task = TaskEx.RunEx(async () =&gt; await MyAsyncMethod()); var result = task.WaitAndUnwrapException(); </code></pre> <p>However, this solution requires a <code>MyAsyncMethod</code> that will work in the thread pool context. So it can't update UI elements or access the ASP.NET request context. And in that case, you may as well add <code>ConfigureAwait(false)</code> to its <code>await</code> statements, and use solution A.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload