Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to serialize async/await?

Let's suppose I have this simple snippet:

async void button_Click(object sender, RoutedEventArgs e)
{
    await Task.Factory.StartNew(() =>
    {
        Console.WriteLine("start");
        Thread.Sleep(5000);
        Console.WriteLine("end");
    });
}

Obviously, everytime I push that button a new task is started even when a previous task still runs. How would I postpone any new task until all previous tasks have finished?

Some more details:

In the example above, each new task is identical to the task before. However, in the original context the sequence of tasks matters: Parameters may change (I could "simulate" it by using DateTime.Now.Ticks). The tasks should be executed in the order they are "registered". Specificly, my program will talk to a serial device. I've done this before with a background thread utilizing a BlockingCollection. However, this time there's a strict request/response-protocol and I'd like to use async/await if it is possible.

Possible solution:

I could imagine creating tasks and storing them in a list. But how would I execute the tasks with respect to the requirements? Or should I return to the thread-based solution I have used before?

like image 306
JeffRSon Avatar asked Jan 16 '14 13:01

JeffRSon


2 Answers

I recommend using a SemaphoreSlim for synchronization. However, you want to avoid Task.Factory.StartNew (as I explain on my blog), and also definitely avoid async void (as I explain in the MSDN article).

private SemaphoreSlim _mutex = new SemaphoreSlim(1);
async void button_Click(object sender, RoutedEventArgs e)
{
  await Task.Run(async () =>
  {
    await _mutex.WaitAsync();
    try
    {
      Console.WriteLine("start");
      Thread.Sleep(5000);
      Console.WriteLine("end");
    }
    finally
    {
      _mutex.Release();
    }
  });
}
like image 51
Stephen Cleary Avatar answered Sep 28 '22 03:09

Stephen Cleary


You could wait on a SemaphoreSlim asynchronously and release it once the job is done. Don't forget to configure the semaphore initialcount to 1.

private static SemaphoreSlim semaphore = new SemaphoreSlim(1);

private async static void DoSomethingAsync()
{
     await semaphore.WaitAsync();
     try
     {
        await Task.Factory.StartNew(() =>
        {
            Console.WriteLine("start");
            Thread.Sleep(5000);
            Console.WriteLine("end");
        });
     }
     finally
     {
        semaphore.Release();
     }
}

private static void Main(string[] args)
{
    DoSomethingAsync();
    DoSomethingAsync();
    Console.Read();
}
like image 22
Sriram Sakthivel Avatar answered Sep 28 '22 04:09

Sriram Sakthivel