title: Limiting the number of parallel task with SemaphoreSlim - why does it work?
body: <p>in MS Docu you can read about SemaphoreSlim:
  „Represents a lightweight alternative to Semaphore that limits the number of threads that can access a resource or pool of resources concurrently.“<br />
  <a href="https://docs.microsoft.com/en-us/dotnet/api/system.threading.semaphoreslim?view=net-5.0" rel="nofollow noreferrer">https://docs.microsoft.com/en-us/dotnet/api/system.threading.semaphoreslim?view=net-5.0</a><br /></p>
  <p>In my understanding a Task is different from Thread. Task is higher level than Thread. Different tasks can run on the same thread. Or a task can be continued on another thread than it was started on.<br />
  (Compare: &quot;server-side applications in .NET using asynchrony will use very few threads without limiting themselves to that. If everything really can be served by a single thread, it may well be - if you never have more than one thing to do in terms of physical processing, then that's fine.&quot;
  from <a href="https://stackoverflow.com/questions/16255539/in-c-sharp-how-to-run-method-async-in-the-same-thread">in C# how to run method async in the same thread</a>)</p>
  <p>IMO if you put this information together, the conclusion is that you can’t limit the number of Tasks running in parallel with the use of a semaphore slim, but…</p>
  <ul>
  <li>there are other texts that give this kind of advice (<a href="https://stackoverflow.com/questions/10806951/how-to-limit-the-amount-of-concurrent-async-i-o-operations">How to limit the amount of concurrent async I/O operations?</a>, see “You can definitely do this…”)</li>
  <li>if I’m executing this code on my machine it seems it IS possible. If I work with different numbers for _MaxDegreeOfParallelism and different ranges of numbers, _RunningTasksCount doesn’t exceed the limit that is given by MaxDegreeOfParallelism.</li>
  </ul>
  <p>Can somebody provide me some information to clearify?</p>
  <pre><code>   class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine(&quot;Hello World!&quot;);
  
              IRunner runner = new RunnerSemaphore();
              runner.Run();
  
              Console.WriteLine(&quot;Hit any key to close...&quot;);
              Console.ReadLine();
          }
      }
      public class RunnerSemaphore : IRunner
      {
          private readonly SemaphoreSlim _ConcurrencySemaphore;
          private List&lt;int&gt; _Numbers;
          private int _MaxDegreeOfParallelism = 3;
          private object _RunningTasksLock = new object();
          private int _RunningTasksCount = 0;
  
          public RunnerSemaphore()
          {
              _ConcurrencySemaphore = new SemaphoreSlim(_MaxDegreeOfParallelism);
              _Numbers = _Numbers = Enumerable.Range(1, 100).ToList();
          }
  
          public void Run()
          {
              RunAsync().Wait();
          }        
  
          private async Task RunAsync()
          {
              List&lt;Task&gt; allTasks = new List&lt;Task&gt;();            
  
              foreach (int number in _Numbers)
              {
                  var task = Task.Run
                      (async () =&gt;
                      {
                          await _ConcurrencySemaphore.WaitAsync();
  
                          bool isFast = number != 1; 
                          int delay = isFast ? 200 : 10000;
  
                          Console.WriteLine($&quot;Start Work {number}\tManagedThreadId {Thread.CurrentThread.ManagedThreadId}\tRunning {IncreaseTaskCount()} tasks&quot;);
                          await Task.Delay(delay).ConfigureAwait(false);
                          Console.WriteLine($&quot;End Work {number}\tManagedThreadId {Thread.CurrentThread.ManagedThreadId}\tRunning {DecreaseTaskCount()} tasks&quot;);
                      })
                      .ContinueWith((t) =&gt;
                      {
                          _ConcurrencySemaphore.Release();
                      });
  
  
                  allTasks.Add(task);
              }
  
              await Task.WhenAll(allTasks.ToArray());
          }
  
          private int IncreaseTaskCount()
          {
              int taskCount;
              lock (_RunningTasksLock)
              {                
                  taskCount = ++ _RunningTasksCount;
              }
              return taskCount;
          }
  
          private int DecreaseTaskCount()
          {
              int taskCount;
              lock (_RunningTasksLock)
              {
                  taskCount = -- _RunningTasksCount;
                   
              }
              return taskCount;
          }        
      }
  </code></pre>