Semaphore Slim and Async

2016/08/16

no comments

Semaphore Slim and Async

On this post I will focus on async waiting on Semaphore (using SemaphoreSlim).

In general .NET is having 2 Semaphore implementations:

Semaphore which is the legacy implementation (but support cross process operations)

SemaphoreSlim which is lightweight version. It bring some performance improvements but it don’t support cross process operation.

this post will focus on SemaphoreSlim WaitAsync API.

Semaphore, Parallel, Async, Wait, Thread, Pool, Task

Let start analyzing the following code snippet, think what’s wrong with it:

Code Snippet
  1. private static SemaphoreSlim _gate = new SemaphoreSlim(4);
  2. static void Main(string[] args)
  3. {
  4.     for (int i = 0; i < 10; i++)
  5.     {
  6.         char c = (char)('A' + i);
  7.         Task t = ExecAsync(c, i);
  8.     }
  9.     Console.WriteLine("Keep working …");
  10.     Console.ReadKey();
  11. }
  12.  
  13. private static async Task ExecAsync(char c, int indent)
  14. {
  15.     try
  16.     {
  17.         _gate.Wait();
  18.         for (int i = 0; i < 3; i++)
  19.         {
  20.             string left = new string('\t', indent);
  21.             Console.WriteLine($"{left}{c},"); // $"{c}," is C#6 syntax
  22.             await Task.Delay(300);
  23.         }
  24.     }
  25.     finally
  26.     {
  27.         _gate.Release();
  28.     }
  29. }

In general the snippet is starting ExecAsyc 10 times, but after the 4th iteration

it will get blocked until one of the previous iteration will reach the finally section and release the semaphore.

In result “Keep working” (on line 9) will wait until the 10 iteration will pass (line 22)

Only then, the main thread will be release, you can read more about when it get released here and here.

 

Let’s think what would had happen if the blocked thread would be a thread pool’s thread rather the main thread?

On this case it would cause Thread Pool starvation, because the blocking thread won’t return to the Pool while waiting.

This starvation may damage the overall performance of your process .

For example think about services, both Web API and WCF is handling the incoming request on the Thread Pool.

 

The solution is simple and straight forward, the the following code snippet:

Code Snippet
  1. private static async Task ExecAsync(char c, int indent)
  2. {
  3.     try
  4.     {
  5.         await _gate.WaitAsync();
  6.         for (int i = 0; i < 3; i++)
  7.         {
  8.             string left = new string('\t', indent);
  9.             Console.WriteLine($"{left}{c},"); // $"{c}," is C#6 syntax
  10.             await Task.Delay(300);
  11.         }
  12.     }
  13.     finally
  14.     {
  15.         _gate.Release();
  16.     }
  17. }

All you have to do is awaiting WaitAsync instead of blocking on Wait (line 5).

When speaking on Thread Pool the tread will return to the pool until the lock will be released.

Remember that Task by default will use Thread from the Thread Pool (default TaskScheduler).

 

Summary:

WaitAsync is a small feature  but it represent more general pattern. you should always

prefer awaiting on async operation rather blocking on synchronous operations.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*