它有什么区别 - 运行 一个 'async' 动作委托和一个 Task.Run(与默认动作委托相比)?

What difference does it make - running an 'async' action delegate with a Task.Run (vs default action delegate)?

我正在努力了解 async/await 并且认为我确实了解一些关于用法的事情。但是仍然不太清楚在下面这样的情况下实际的好处是什么。

查看Task.Run用法。第一种方法使用普通委托并使用 Thread.Sleep 但第二种方法使用 'async' 委托和 Task.Delay.

我的问题是:这对这个方法有什么影响(或没有影响)?

该方法本身是一个异步方法。该代码正在创建一个单独的线程(通过 Task.Run),并且该线程除了执行该委托之外别无他法。因此,即使它在 Task.Delay 上通过等待产生,在这种情况下有什么用,因为线程无论如何都是一个孤立的线程,不用于任何其他事情,即使它只使用 Thread.Sleep,线程仍会进行上下文切换以让给处理器的其他线程。

// The task thread uses a async delegate
public async Task<bool> RetrySendEmail(MailMessage message)
{
      bool emailSent = false;
      await (Task.Run(***async ()*** =>
      {
            for (int i = 0; i < 3; i++)
            {
                 if (emailSent)
                      break;
                 else
                      // Wait for 5 secs before trying again
                      ***await Task.Delay(5000);***

                 try
                 {
                      Smtphost.Send(message);
                      emailSent = true;
                      break;
                 }
                 catch (Exception e) { emailSent = false; // log; }
            }
            return emailSent;
      }));
}

// The task thread uses a normal delegate 
public async Task<bool> RetrySendEmail(MailMessage message)
{
      bool emailSent = false;
      await (Task.Run(***()*** =>
      {
            for (int i = 0; i < 3; i++)
            {
                 if (emailSent)
                      break;
                 else
                      // Wait for 5 secs before trying again
                      ***Thread.Sleep(5000);***

                 try
                 {
                      Smtphost.Send(message);
                      emailSent = true;
                      break;
                 }
                 catch (Exception e){ emailSent = false; // log; }
            }
                 return emailSent;
        }));
}

区别在于您正在浪费线程及其分配的时间片。

当您阻塞一个线程 5 秒后,该线程将无法在系统的其他部分用于执行实际 CPU 工作。它还会创建上下文切换,因为该线程无法执行任何其他操作。

当您使用 Task.Delay 而不是 Thread.Sleep 释放该线程时,线程可以 return 到 ThreadPool,获取等待任务并执行它。

总的来说,当您释放线程时,您可以使您的应用程序更具可扩展性和效率,因为它需要更少的资源来完成相同的工作,或者需要相同数量的资源来完成更多的工作。


如果您的操作确实是异步的,则无需使用 Task.Run(除非您需要后台线程)。您可以只调用该方法并等待 returned 任务:

public async Task<bool> RetrySendEmail(MailMessage message)
{
    bool emailSent = false;
    for (int i = 0; i < 3; i++)
    {
         if (emailSent)
              break;
         else
              await Task.Delay(5000);

         try
         {
              Smtphost.Send(message);
              emailSent = true;
              break;
         }
         catch (Exception e) { emailSent = false; // log; }
    }
    return emailSent;
}

My question is : how does this make any difference to this method (or it does not) ?

一些差异

  1. Task.Run 中使用 async 委托意味着您实际上 运行 一个 Task<Task>Task.Run 是异步感知并为你解包内部任务,这对你是隐藏的,这是 Task.Factory.StartNew 没有做的
  2. 当您将异步委托与 Task.Run 一起使用时,您会创建一个新线程,然后在您点击 await Task.Delay 后放弃控制权。延续将 运行 在任意线程池线程上。此外,委托由编译器转换为状态机。

    使用普通委托,您创建一个线程,同步阻塞它 5 秒,然后从您离开的地方继续。没有国家机器,没有让步。


So, even if it yields with an await on Task.Delay, what is the use in this scenario, since the thread is anyways a isolated thread not used for anything else and even if it just uses Thread.Sleep, the thread would still context switch to yield to other threads for the processor.

当您想同时执行 CPU 和 IO 绑定工作时,可以将 asyncTask.Run 一起使用,所有这些都在专用线程中进行。您的想法是正确的,在异步委托产生之后,它 returns 在任意线程上。但是,如果您没有使用 Task.Run,并且 async 方法是从附加了自定义同步上下文(例如 WinformsSynchronizationContext)的线程执行的,那么 await 之后的任何工作都会产生回到 UI 消息循环,除非你使用 ConfigureAwait(false).

说实话,Task.Runasync正确使用的场景我还没见过多少。但有时它确实有意义。