【问题标题】:How to dispose properly using async and await如何使用 async 和 await 正确处理
【发布时间】:2014-08-03 16:38:24
【问题描述】:

我正在尝试将代码从 Thread 替换为 Task。睡眠/延迟仅代表长时间运行的活动。

static void Main(string[] args)
{
    ThreadDoWork();
    TaskDoWork();
}
public static void ThreadDoWork()
{
    using (var dispose = new ThreadDispose())
    {
        dispose.RunAsync();
    }
}
public static async void TaskDoWork()
{
    using (var dispose = new TaskDispose())
    {
        await dispose.RunAsync();
    }
}
public class ThreadDispose : IDisposable
{
    public void RunAsync ()
    {
        ThreadPool.QueueUserWorkItem(state =>
        {
            Thread.Sleep(3000);
        });
    }
    void IDisposable.Dispose()
    {
        File.AppendAllText("D:\\test.txt", "thread disposing");
    }
}
public class TaskDispose : IDisposable
{
    public async Task RunAsync()
    {
        await Task.Delay(3000);
    }
    void IDisposable.Dispose()
    {
        File.AppendAllText("D:\\test.txt", "task disposing");
    }
}

test.txt 3秒后的结果只有

线程处理

为了让TaskDispose::Dispose 始终像ThreadDispose 一样执行,我需要更改什么?

【问题讨论】:

  • 你是否意识到你的 ThreadDispose 在它完成工作之前就被处理掉了?我想如果你给它时间来实际完成,这个任务也会被处理掉。
  • 是的,我意识到,任务概念对我来说有点新,所以请告诉我如何用任务代码替换线程代码
  • 尝试将 public static async void TaskDoWork() 更改为 public static async Task TaskDoWork() 并在 Main 中等待它...
  • @AnthonyChu,同样的结果,主要的TaskDoWork(); 是警告Because this code is not awaited..,我试图只是await TaskDoWork(); 并将Main 更改为static async void Main,但它编译错误

标签: c# .net multithreading async-await dispose


【解决方案1】:

让我们隔离每一段代码:

public static void ThreadDoWork()
{
    using (var dispose = new ThreadDispose())
    { 
        dispose.RunAsync();
    }
}

public void RunAsync()
{
    ThreadPool.QueueUserWorkItem(state =>
    {
        Thread.Sleep(3000);
    });
}

您在第一段代码中所做的是线程池线程上的队列工作。因为您在using 范围内运行此代码,并且它在不同的线程上异步运行,所以它立即 处理。这就是您在文本文件中看到 dispose 消息的原因。

public static async void TaskDoWork()
{
   using (var dispose = new TaskDispose())
   {
       await dispose.RunAsync();
   }
}

public class TaskDispose : IDisposable
{
   public async Task RunAsync()
   {
       await Task.Delay(3000);
   }
}

当你在你的方法中await时,你实际上说的是:“执行这段代码。因为它本质上是异步的,我会将控制权返回给调用方法,请打电话给我完成异步操作后返回"

您的代码点击了await 关键字并将控制权返回给您的Main 方法。在Main 内部,您的异步方法是要执行的最后一段代码,从而完成您的应用程序,并且不会给您的Dispose 方法执行机会。

如果你想处理它,你必须将返回类型从void 更改为Task 并明确地Wait

public static async Task TaskDoWork()
{
    using (var dispose = new TaskDispose())
    {
       await dispose.RunAsync();
    }
}

现在:

static void Main(string[] args)
{
    ThreadDoWork();
    TaskDoWork().Wait();
}

旁注:

应该遵循一些准则:

  1. async void 是为了与事件处理程序兼容,很少有超出该范围的场合应该使用它。请改用async Task

  2. 使用 TAP(任务异步模式)执行异步操作的方法应以 Async 后缀结尾。 TaskDoWork 应该是 TaskDoWorkAsync

  3. Task 上使用Wait 可能会导致死锁。在这种特殊情况下,它不是因为控制台应用程序没有SynchronizationContext 并使用线程池。推荐的方法是“一直异步”并使用await

async-await tag wiki 内有很棒的阅读材料,请务必查看。

【讨论】:

    【解决方案2】:

    这两个例子都有问题。 首先,在线程情况下,Dispose 方法在线程结束之前调用。 在任务的情况下,当任务完成时会调用 Dispose 方法。

    但是文件只有线程case写入的文本,因为它是在调用RunAsync方法后同步调用的。而且Task案例没有写任何东西,因为应用程序在任务完成之前就结束了。

    测试以等待任务完成,例如在 Main 方法结束时使用 Thread.Sleep(5000),并且任务应写入输出文件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-02-14
      • 2020-08-23
      • 2018-04-13
      • 2021-04-10
      • 1970-01-01
      • 2021-12-31
      • 2013-10-02
      相关资源
      最近更新 更多