title: Limiting the amount of concurrent tasks in .NET 4.5
body: <p>Observer the following function:</p>
  
  <pre><code>public Task RunInOrderAsync&lt;TTaskSeed&gt;(IEnumerable&lt;TTaskSeed&gt; taskSeedGenerator,
      CreateTaskDelegate&lt;TTaskSeed&gt; createTask,
      OnTaskErrorDelegate&lt;TTaskSeed&gt; onError = null,
      OnTaskSuccessDelegate&lt;TTaskSeed&gt; onSuccess = null) where TTaskSeed : class
  {
      Action&lt;Exception, TTaskSeed&gt; onFailed = (exc, taskSeed) =&gt;
      {
          if (onError != null)
          {
              onError(exc, taskSeed);
          }
      };
  
      Action&lt;Task&gt; onDone = t =&gt;
      {
          var taskSeed = (TTaskSeed)t.AsyncState;
          if (t.Exception != null)
          {
              onFailed(t.Exception, taskSeed);
          }
          else if (onSuccess != null)
          {
              onSuccess(t, taskSeed);
          }
      };
  
      var enumerator = taskSeedGenerator.GetEnumerator();
      Task task = null;
      while (enumerator.MoveNext())
      {
          if (task == null)
          {
              try
              {
                  task = createTask(enumerator.Current);
                  Debug.Assert(ReferenceEquals(task.AsyncState, enumerator.Current));
              }
              catch (Exception exc)
              {
                  onFailed(exc, enumerator.Current);
              }
          }
          else
          {
              task = task.ContinueWith((t, taskSeed) =&gt;
              {
                  onDone(t);
                  var res = createTask((TTaskSeed)taskSeed);
                  Debug.Assert(ReferenceEquals(res.AsyncState, taskSeed));
                  return res;
              }, enumerator.Current).TaskUnwrap();
          }
      }
  
      if (task != null)
      {
          task = task.ContinueWith(onDone);
      }
  
      return task;
  }
  </code></pre>
  
  <p>Where <code>TaskUnwrap</code> is the state preserving version of the standard <code>Task.Unwrap</code>:</p>
  
  <pre><code>public static class Extensions
  {
      public static Task TaskUnwrap(this Task&lt;Task&gt; task, object state = null)
      {
          return task.Unwrap().ContinueWith((t, _) =&gt;
          {
              if (t.Exception != null)
              {
                  throw t.Exception;
              }
          }, state ?? task.AsyncState);
      }
  }
  </code></pre>
  
  <p>The <code>RunInOrderAsync</code> method allows to run N tasks asynchronously, but sequentially - one after another. In effect, it runs the tasks created from the given seeds with the concurrency limit of 1. </p>
  
  <p>Let us assume that the tasks created from the seeds by the <code>createTask</code> delegate do not correspond themselves to multiple concurrent tasks.</p>
  
  <p>Now, I would like to throw in the maxConcurrencyLevel parameter, so the function signature would look like this:</p>
  
  <pre><code>Task RunInOrderAsync&lt;TTaskSeed&gt;(int maxConcurrencyLevel,
    IEnumerable&lt;TTaskSeed&gt; taskSeedGenerator,
    CreateTaskDelegate&lt;TTaskSeed&gt; createTask,
    OnTaskErrorDelegate&lt;TTaskSeed&gt; onError = null,
    OnTaskSuccessDelegate&lt;TTaskSeed&gt; onSuccess = null) where TTaskSeed : class
  </code></pre>
  
  <p>And here I am a bit stuck.</p>
  
  <p>The SO has questions like these:</p>
  
  <ul>
  <li><a href="https://stackoverflow.com/questions/2898609/system-threading-tasks-limit-the-number-of-concurrent-tasks">System.Threading.Tasks - Limit the number of concurrent Tasks</a></li>
  <li><a href="https://stackoverflow.com/questions/12366608/task-based-processing-with-a-limit-for-concurrent-task-number-with-net-4-5-and">Task based processing with a limit for concurrent task number with .NET 4.5 and c#</a></li>
  <li><a href="https://stackoverflow.com/questions/9315937/net-tpl-limited-concurrency-level-task-scheduler-with-task-priority">.Net TPL: Limited Concurrency Level Task scheduler with task priority?</a></li>
  </ul>
  
  <p>Which basically propose two ways to attack the problem:</p>
  
  <ol>
  <li>Using <code>Parallel.ForEach</code> with <code>ParallelOptions</code> specifying the <code>MaxDegreeOfParallelism</code> property value as equal to the desired max concurrency level.</li>
  <li>Using a custom <code>TaskScheduler</code> with the desired <code>MaximumConcurrencyLevel</code> value.</li>
  </ol>
  
  <p>The second approach doesn't cut it, because all the tasks involved must use the same task scheduler instance. For that, all the methods used to return a <code>Task</code> must have an overload accepting the custom <code>TaskScheduler</code> instance. Unfortunately, Microsoft is not very consistent with that respect. For instance, <code>SqlConnection.OpenAsync</code> does not accept such an argument (but <code>TaskFactory.FromAsync</code> does).</p>
  
  <p>The first approach implies that I will have to convert tasks to actions, something like this:</p>
  
  <pre><code>() =&gt; t.Wait()
  </code></pre>
  
  <p>I am not sure it is a good idea, but I will be glad to get more input on that.</p>
  
  <p>Another approach is to utilize <code>TaskFactory.ContinueWhenAny</code>, but that is messy.</p>
  
  <p>Any ideas?</p>
  
  <p><strong>EDIT 1</strong></p>
  
  <p>I would like to clarify the reasons for wanting the limit. Our tasks ultimately execute SQL statements against the same SQL server. What we want is a way to limit the amount of concurrent outgoing SQL statements. It is entirely possible that there will be other SQL statements executing concurrently from other pieces of code, but this one is a batch processor and could potentially flood the server.</p>
  
  <p>Now, be advised, that although we are talking about the same SQL server, there are numerous databases on that same server. So, it is not about limiting the amount of open SQL connections to the same database, because the database may not be the same at all.</p>
  
  <p>That is why doom's day solutions like <code>ThreadPool.SetMaxThreads()</code> are irrelevant.</p>
  
  <p>Now, about <code>SqlConnection.OpenAsync</code>. It was made asynchronous for a reason - it might make a roundtrip to the server and thus might be subject to the network latency and other lovely side effects of distributed environment. As such it is no different than other async methods which do accept the <code>TaskScheduler</code> parameter. I tend to think that not accepting one is just a bug.</p>
  
  <p><strong>EDIT 2</strong></p>
  
  <p>I would like to preserve the asynchronous spirit of the original function. Hence I wish to avoid any explicit blocking solutions.</p>
  
  <p><strong>EDIT 3</strong></p>
  
  <p>Thanks to <a href="https://stackoverflow.com/a/20357517/263693">@fsimonazzi's answer</a> I now have a working implementation of the desired functionality. Here is the code:</p>
  
  <pre><code>        var sem = new SemaphoreSlim(maxConcurrencyLevel);
          var tasks = new List&lt;Task&gt;();
  
          var enumerator = taskSeedGenerator.GetEnumerator();
          while (enumerator.MoveNext())
          {
              tasks.Add(sem.WaitAsync().ContinueWith((_, taskSeed) =&gt;
              {
                  Task task = null;
                  try
                  {
                      task = createTask((TTaskSeed)taskSeed);
                      if (task != null)
                      {
                          Debug.Assert(ReferenceEquals(task.AsyncState, taskSeed));
                          task = task.ContinueWith(t =&gt;
                          {
                              sem.Release();
                              onDone(t);
                          });
                      }
                  }
                  catch (Exception exc)
                  {
                      sem.Release();
                      onFailed(exc, (TTaskSeed)taskSeed);
                  }
                  return task;
              }, enumerator.Current).TaskUnwrap());
          }
  
          return Task.Factory.ContinueWhenAll(tasks.ToArray(), _ =&gt; sem.Dispose());
  </code></pre>