1

I am looking for the best practices to synchronize Tasks (not even sure that this is semantically right, I should probably say synchronize Threads with async code).

Take the following code:

Task.Run(async () => await lib.WaitAsync());
Task.Run(async () => await lib.WaitAsync());
Task.Run(async () => await lib.WaitAsync());

// Synchronize all waiters
lib.Release();

// All Tasks shall be completed

When working with Threads, I used to do that with ManualResetEvent. So my first (simplified) approach would be:

Task WaitAsync()
{
   await Task.Run(() => manualResetEvent.WaitOne());
} 

void Release()
{
   manualResetEvent.Set();
} 

Now I see that people have made their own implementation of async ManualResetEvent.

What is wrong with the original approach and why would someone prefer an async implementation ? Is it because of the cancellation ? The exceptions unwrapping/handling ?

And more generally, is there any disadvantage when using the regular Thread synchronization objects (Mutex, Semaphore, ...) with async code ?

Thank you

Cem.S
  • 1,001
  • 10
  • 15
  • 4
    `What is wrong with the original approach` - assuming the original code is the one that uses `manualResetEvent`, it's burning a thread on waiting when it [does not have to](https://blog.stephencleary.com/2013/11/there-is-no-thread.html). The code prior to it, with several `Task.Run`s, is also not great because it's [fire and forget](https://stackoverflow.com/q/46053175/11683). And it's probably worth mentioning that [async is not about threads](https://stackoverflow.com/q/17661428/11683). – GSerg Dec 12 '19 at 08:48
  • With Async code you should almost never use `Task.Run()` to execute an async method. – Erik Philips Dec 12 '19 at 08:49

1 Answers1

5

An asynchronous Task may use multiple threads when progressing from one internal asynchronous operation to the next, and may not utilize any thread at all for most of its lifetime. This is desired, because it promotes scalability. Blocking threads is a waste of resources. And all threading synchronization primitives (Mutex, Semaphore, WaitHandle etc) are doing just that, they block threads.

On the contrary no threads are blocked when throttling asynchronous operations with SemaphoreSlim.WaitAsync, which is the only available built-in mechanism for blocking tasks asynchronously AFAIK.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • 1
    Thank you, and thanks GSerg as well. This is exactly the type of information I needed : when a Task is awaited, the current worker isn't blocked and continues other processing whereas a mutex/semaphore would block the thread. Now it make totally sense to avoid using primitive synchronization objects with async code. – Cem.S Dec 12 '19 at 09:52