title: Why is TaskScheduler.Current the default TaskScheduler?
body: <p>The Task Parallel Library is great and I've used it a lot in the past months. However, there's something really bothering me: the fact that <a href="http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.current.aspx" rel="noreferrer"><code>TaskScheduler.Current</code></a> is the default task scheduler, not <a href="http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.default.aspx" rel="noreferrer"><code>TaskScheduler.Default</code></a>. This is absolutely not obvious at first glance in the documentation nor samples.</p>
  
  <p><code>Current</code> can lead to subtle bugs since its behavior is changing depending on whether you're inside another task. Which can't be determined easily.</p>
  
  <p>Suppose I am writting a library of asynchronous methods, using the standard async pattern based on events to signal completion on the original synchronisation context, in the exact same way XxxAsync methods do in the .NET Framework (eg <code>DownloadFileAsync</code>). I decide to use the Task Parallel Library for implementation because it's really easy to implement this behavior with the following code:</p>
  
  <pre><code>public class MyLibrary
  {
      public event EventHandler SomeOperationCompleted;
  
      private void OnSomeOperationCompleted()
      {
          SomeOperationCompleted?.Invoke(this, EventArgs.Empty);
      }
  
      public void DoSomeOperationAsync()
      {
          Task.Factory.StartNew(() =&gt;
          {
              Thread.Sleep(1000); // simulate a long operation
          }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
          .ContinueWith(t =&gt;
          {
              OnSomeOperationCompleted(); // trigger the event
          }, TaskScheduler.FromCurrentSynchronizationContext());
      }
  }
  </code></pre>
  
  <p>So far, everything works well. Now, let's make a call to this library on a button click in a WPF or WinForms application:</p>
  
  <pre><code>private void Button_OnClick(object sender, EventArgs args)
  {
      var myLibrary = new MyLibrary();
      myLibrary.SomeOperationCompleted += (s, e) =&gt; DoSomethingElse();
      myLibrary.DoSomeOperationAsync(); // call that triggers the event asynchronously
  }
  
  private void DoSomethingElse() // the event handler
  {
      //...
      Task.Factory.StartNew(() =&gt; Thread.Sleep(5000)); // simulate a long operation
      //...
  }
  </code></pre>
  
  <p>Here, the person writing the library call chose to start a new <code>Task</code> when the operation completes. Nothing unusual. He or she follows examples found everywhere on the web and simply use <code>Task.Factory.StartNew</code> without specifying the <code>TaskScheduler</code> (and there is no easy overload to specify it at the second parameter). The <code>DoSomethingElse</code> method works fine when called alone, but as soon at it's invoked by the event, the UI freezes since <code>TaskFactory.Current</code> will reuse the synchronization context task scheduler from my library continuation.</p>
  
  <p>Finding out this could take some time, especially if the second task call is buried down in some complex call stack. Of course, the fix here is simple once you know how everything works: always specify <code>TaskScheduler.Default</code> for any operation you're expecting to be running on the thread pool. However, maybe the second task is started by another external library, not knowing about this behavior and naively using <code>StartNew</code> without a specific scheduler. I'm expecting this case to be quite common.</p>
  
  <p>After wrapping my head around it, I can't understand the choice of the team writing the TPL to use <code>TaskScheduler.Current</code> instead of <code>TaskScheduler.Default</code> as the default:</p>
  
  <ul>
  <li>It's not obvious at all, <code>Default</code> is not the default! And the documentation is seriously lacking.</li>
  <li>The real task scheduler used by <code>Current</code> depends of the call stack! It's hard to maintain invariants with this behavior.</li>
  <li>It's cumbersome to specify the task scheduler with <code>StartNew</code> since you have to specify the task creation options and cancellation token first, leading to long, less readable lines. This can be alleviated by writing an extension method or creating a <code>TaskFactory</code> that uses <code>Default</code>.</li>
  <li>Capturing the call stack has additional performance costs.</li>
  <li>When I really want a task to be dependent on another parent running task, I prefer to specify it explicitly to ease code reading rather than rely on call stack magic.</li>
  </ul>
  
  <p>I know this question may sound quite subjective, but I can't find a good objective argument as to why this behavior is as it. I'm sure I'm missing something here: that's why I'm turning to you.</p>