Note that there are some explanatory texts on larger screens.

plurals
  1. POThe lack of non-capturing Task.Yield forces me to use Task.Run, why follow that?
    text
    copied!<p>Apologies in advance if this question is opinion-based. The lack of <a href="http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.yield.aspx" rel="nofollow noreferrer">Task.Yield</a> version which wouldn't capture the execution context was already discussed <a href="https://stackoverflow.com/questions/8742522/taskex-yieldtaskscheduler">here</a>. <strong>Apparently, this feature was present in some form in early versions of Async CTP but was removed because it could easily be misused</strong>. </p> <p>IMO, such feature could be as easily misused as <code>Task.Run</code> itself. Here's what I mean. Imagine there's an awaitable <code>SwitchContext.Yield</code> API which schedules the continuation on ThreadPool, so the execution will always continues on a thread different from the calling thread. I could have used it in the following code, which starts some CPU-bound work from a UI thread. I would consider it a convenient way of continuing the CPU-bound work on a pool thread:</p> <pre><code>class Worker { static void Log(string format, params object[] args) { Debug.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, String.Format(format, args)); } public async Task UIAction() { // UI Thread Log("UIAction"); // start the CPU-bound work var cts = new CancellationTokenSource(5000); var workTask = DoWorkAsync(cts.Token); // possibly await for some IO-bound work await Task.Delay(1000); Log("after Task.Delay"); // finally, get the result of the CPU-bound work int c = await workTask; Log("Result: {0}", c); } async Task&lt;int&gt; DoWorkAsync(CancellationToken ct) { // start on the UI thread Log("DoWorkAsync"); // switch to a pool thread and yield back to the UI thread await SwitchContext.Yield(); Log("after SwitchContext.Yield"); // continue on a pool thread int c = 0; while (!ct.IsCancellationRequested) { // do some CPU-bound work on a pool thread: counting cycles :) c++; // and use async/await too await Task.Delay(50); } return c; } } </code></pre> <p>Now, without <code>SwitchContext.Yield</code>, <code>DoWorkAsync</code> would look like below. It adds some extra level of complexity in form of async delegate and task nesting:</p> <pre><code>async Task&lt;int&gt; DoWorkAsync(CancellationToken ct) { // start on the UI thread Log("DoWorkAsync"); // Have to use async delegate // Task.Run uwraps the inner Task&lt;int&gt; task return await Task.Run(async () =&gt; { // continue on a pool thread Log("after Task.Yield"); int c = 0; while (!ct.IsCancellationRequested) { // do some CPU-bound work on a pool thread: counting cycles :) c++; // and use async/await too await Task.Delay(50); } return c; }); } </code></pre> <p>That said, implementing <code>SwitchContext.Yield</code> may actually be quite simple and (I dare to say) efficient:</p> <pre><code>public static class SwitchContext { public static Awaiter Yield() { return new Awaiter(); } public struct Awaiter : System.Runtime.CompilerServices.INotifyCompletion { public Awaiter GetAwaiter() { return this; } public bool IsCompleted { get { return false; } } public void OnCompleted(Action continuation) { ThreadPool.QueueUserWorkItem((state) =&gt; ((Action)state)(), continuation); } public void GetResult() { } } } </code></pre> <p>So, <strong>my question is</strong>, why should I prefer the second version of <code>DoWorkAsync</code> over the first one, and why would using <code>SwitchContext.Yield</code> be considered a bad practice?</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