【问题标题】:When should Task.Run() be used?什么时候应该使用 Task.Run()?
【发布时间】:2015-02-20 02:10:07
【问题描述】:

这两种方法有什么区别:

public static int Main(string[] args)
{
    string result;  

    Task.Run(async () =>
    {
        Task<string> getStringTask = GetStringAsync();
        result = await validationsTask;
    }).Wait();

    Console.WriteLine(result);
}

public static int Main(string[] args)
{
    Task<string> getStringTask = GetStringAsync();
    getStringTask.Wait();
    string result = getStringTask.Result;

    Console.WriteLine(result);
}

我看到很多人使用第一种方法,但我不知道为什么。有什么特别的优势吗?推荐使用哪一个来等待控制台应用程序的 main 中的异步方法?

【问题讨论】:

    标签: c# asynchronous concurrency task


    【解决方案1】:

    有什么特别的优势吗?

    通常使用async 方法,操作被同步初始化,然后等待可以与await 异步或与Wait() 同步。 Main 方法不能是async,所以你必须在那里用Wait() 阻止,或者你可以执行Console.ReadKey() 来运行直到用户按下一个键。

    Task.Run(async () =&gt; ... )async 操作的初始化成本很高时非常有用。这样一来,您就可以在操作初始化时允许主线程继续运行。

    在控制台应用程序的 main 中等待异步方法推荐使用哪一个?

    我会使用第二种方法的略微修改版本。您可以添加一个MainAsync 方法并从Main 调用它,然后您可以在其中使用await

    public static async Task MainAsync()
    {
        string result = await GetStringAsync();    
        Console.WriteLine(result);
    }
    
    public static int Main(string[] args)
    {
        MainAsync().Wait();
    }
    

    对于控制台应用程序,也没有死锁风险,因为没有SynchronizationContext 并且使用了默认线程池。

    【讨论】:

      【解决方案2】:

      第一种方法在异步函数完成后使用线程池线程继续执行,而第二种方法使用启动异步函数的调用线程继续执行。

      使用第二种方法,可能会出现死锁。例如(类似于CLR via C#)书中摘录的一个例子:

          public static int Main(string[] args)
          {
              Task<string> getStringTask = GetStringAsync();
              string result = getStringTask.Result; //the main thread is blocked waiting.
      
              Console.WriteLine(result);
          }
      
          public Task<string> GetStringAsync()
          {  
              // Issue the HTTP request and let the thread return from GetHttp 
              HttpResponseMessage msg = await new HttpClient().GetAsync("http://Wintellect.com/"); 
              // We never get here: The main thread is waiting for this method to finish but this method 
              // can't finish because the main thread is waiting for it to finish --> DEADLOCK! 
              return await msg.Content.ReadAsStringAsync();
          }
      

      所以第一种方法避免了这个问题:

      public static int Main(string[] args)
      {
          string result;  
      
          Task.Run(async () =>
          {
              // We run on a thread pool thread
              Task<string> getStringTask = GetStringAsync();
              // We do get here because any thread pool thread can execute this code, we don't need the main thread.
              result = await validationsTask;
          }).Wait();
      
          Console.WriteLine(result);
      }
      

      另一种解决方案是使用ConfigureAwait(false),摘自书中:

      将 true 传递给此方法会给您与不调用相同的行为 方法。但是,如果您传递 false,则 await 运算符会 不查询调用线程的 SynchronizationContext 对象,并且,当 线程池线程完成任务,它只是简单地完成它并且 await 算子通过线程池线程执行后的代码。

          public Task<string> GetStringAsync()
          {  
               HttpResponseMessage msg = await new HttpClient().GetAsync("http://Wintellect.com/").ConfigureAwait(false); 
               // We DO get here now because a thread pool can execute this code 
      // as opposed to forcing the main thread to execute it.   
               return await msg.Content.ReadAsStringAsync().ConfigureAwait(false);
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-02
        • 2011-04-15
        • 2017-04-10
        • 2012-03-19
        • 2018-05-12
        • 2018-12-11
        • 1970-01-01
        • 2022-09-28
        相关资源
        最近更新 更多