如何序列化async/await?

Jef*_*Son 10 c# asynchronous async-await

让我们假设我有这个简单的片段:

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

显然,每次按下该按钮,即使前一个任务仍在运行,也会启动一个新任务.在所有先前的任务完成之前,我将如何推迟任何新任务?

更多细节:

在上面的示例中,每个新任务都与之前的任务相同.但是,在原始上下文中,任务序列很重要:参数可能会发生变化(我可以通过使用来"模拟"它DateTime.Now.Ticks).任务应按照"注册"的顺序执行.具体来说,我的程序将与串行设备通信.我之前用背景线程利用了一个BlockingCollection.但是,这次有一个严格的请求/响应协议,如果可能,我想使用async/await.

可能的方法:

我可以想象创建任务并将它们存储在列表中.但是,我如何执行与要求相关的任务?或者我应该回到之前使用的基于线程的解决方案?

Ste*_*ary 8

我建议使用a SemaphoreSlim进行同步.但是,你想避免Task.Factory.StartNew(正如我在我的博客上解释的那样),也绝对避免async void(正如我在MSDN文章中解释的那样).

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();
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

  • @Noseratio:这种方法也有效.IMO的"SemaphoreSlim"代码具有更清晰的意图; 从'await`-previous-task代码中可以看出,复制上一个任务非常重要. (2认同)

Sri*_*vel 7

您可以SemaphoreSlim异步等待并在作业完成后释放它.不要忘记将信号量initialcount配置为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();
}
Run Code Online (Sandbox Code Playgroud)