Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>This behavior is due to an optimization (which is an implementation detail).</p> <p>Specifically, the continuation scheduled by <code>await</code> uses the <code>TaskContinuationOptions.ExecuteSynchronously</code> flag. This is not officially documented anywhere but I did encounter this a few months ago and <a href="http://blog.stephencleary.com/2012/12/dont-block-in-asynchronous-code.html" rel="nofollow">wrote it up on my blog</a>.</p> <p>Stephen Toub has a <a href="http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx" rel="nofollow">blog post that is the best documentation on how <code>ExecuteSynchronously</code> actually works</a>. One important point is that <code>ExecuteSynchronously</code> will not actually execute synchronously if the task scheduler for that continuation is not compatible with the current thread.</p> <p>As you pointed out, console apps don't have a <code>SynchronizationContext</code>, so task continuations scheduled by <code>await</code> will use <code>TaskScheduler.Current</code> (which in this case is <code>TaskScheduler.Default</code>, the thread pool task scheduler).</p> <p>When you start another task via <code>Task.Run</code>, you're explicitly executing it on the thread pool. So when it reaches the end of its method, it completes its returned task, causing the continuation to execute (synchronously). Since the task scheduler captured by <code>await</code> was the thread pool scheduler (and therefore compatible with the continuation), it will just directly execute the next portion of <code>DoSomething</code>.</p> <p>Note that there is a race condition here. The next portion of <code>DoSomething</code> will only execute synchronously if it is <em>already</em> attached as a continuation to the task returned by <code>Task.Run</code>. On my machine, the first <code>Task.Run</code> will resume <code>DoSomething</code> on another thread because the continuation is not attached by the time the <code>Task.Run</code> delegate completes; the second <code>Task.Run</code> does resume <code>DoSomething</code> on the same thread.</p> <p>So I modified the code to be slightly more deterministic; this code:</p> <pre><code>static Task DoSomething() { return Task.Run(async () =&gt; { Console.WriteLine("DoSomething-1 sleeping {0}", Thread.CurrentThread.ManagedThreadId); await Task.Run(() =&gt; { Console.WriteLine("DoSomething-1 sleep {0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(100); }); Console.WriteLine("DoSomething-1 awake {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("DoSomething-2 sleeping {0}", Thread.CurrentThread.ManagedThreadId); var task = Task.Run(() =&gt; { Console.WriteLine("DoSomething-2 sleep {0}", Thread.CurrentThread.ManagedThreadId); }); Thread.Sleep(100); await task; Console.WriteLine("DoSomething-2 awake {0}", Thread.CurrentThread.ManagedThreadId); }); } </code></pre> <p>(on my machine) shows both of the possibilities from the race condition:</p> <pre><code>Start 8 DoSomething-1 sleeping 9 DoSomething-1 sleep 10 DoSomething-1 awake 10 DoSomething-2 sleeping 10 DoSomething-2 sleep 11 DoSomething-2 awake 10 End 8 </code></pre> <p>BTW, your use of <code>Task.Yield</code> is incorrect; you have to <code>await</code> the result to actually do anything.</p> <p>Note that this behavior (<code>await</code> using <code>ExecuteSynchronously</code>) is an undocumented implementation detail and may change in the future.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

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