首页 文章

Task.Start / Wait和Async / Await有什么区别?

提问于
浏览
189

我可能会遗漏一些东西但是做什么之间有什么不同:

public void MyMethod()
{
  Task t = Task.Factory.StartNew(DoSomethingThatTakesTime);
  t.Wait();
  UpdateLabelToSayItsComplete();
}

public async void MyMethod()
{
  var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
  await result;
  UpdateLabelToSayItsComplete();
}

private void DoSomethingThatTakesTime()
{
  Thread.Sleep(10000);
}

6 回答

  • 40

    我可能会遗漏一些东西

    你是 .

    在执行Task.Wait和等待任务之间有什么区别?

    你在餐厅的服务员点了午餐 . 在给出订单后的一刻,一位朋友走进来,坐在你身边,开始交谈 . 现在你有两个选择 . 你可以忽略你的朋友,直到任务完成 - 你可以等到你的汤到来,在你等待的时候什么都不做 . 或者你可以回复你的朋友,当你的朋友停止说话时,服务员会给你带来汤 .

    Task.Wait 阻塞,直到任务完成 - 您将忽略您的朋友,直到任务完成 . await 继续处理消息队列中的消息,当任务完成时,它会将消息排入"pick up where you left off after that await" . 你和你的朋友谈话,当谈话休息时,汤就到了 .

  • 107

    为了演示Eric的答案,这里有一些代码:

    public void ButtonClick(object sender, EventArgs e)
    {
      Task t = new Task.Factory.StartNew(DoSomethingThatTakesTime);
      t.Wait();  
      //If you press Button2 now you won't see anything in the console 
      //until this task is complete and then the label will be updated!
      UpdateLabelToSayItsComplete();
    }
    
    public async void ButtonClick(object sender, EventArgs e)
    {
      var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
      await result;
      //If you press Button2 now you will see stuff in the console and 
      //when the long method returns it will update the label!
      UpdateLabelToSayItsComplete();
    }
    
    public void Button_2_Click(object sender, EventArgs e)
    {
      Console.WriteLine("Button 2 Clicked");
    }
    
    private void DoSomethingThatTakesTime()
    {
      Thread.Sleep(10000);
    }
    
  • 6

    这个例子非常清楚地展示了这种差异使用async / await,调用线程不会阻塞并继续执行 .

    static void Main(string[] args)
    {
        WriteOutput("Program Begin");
        // DoAsTask();
        DoAsAsync();
        WriteOutput("Program End");
        Console.ReadLine();
    }
    
    static void DoAsTask()
    {
        WriteOutput("1 - Starting");
        var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
        WriteOutput("2 - Task started");
        t.Wait();
        WriteOutput("3 - Task completed with result: " + t.Result);
    }
    
    static async Task DoAsAsync()
    {
        WriteOutput("1 - Starting");
        var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
        WriteOutput("2 - Task started");
        var result = await t;
        WriteOutput("3 - Task completed with result: " + result);
    }
    
    static int DoSomethingThatTakesTime()
    {
        WriteOutput("A - Started something");
        Thread.Sleep(1000);
        WriteOutput("B - Completed something");
        return 123;
    }
    
    static void WriteOutput(string message)
    {
        Console.WriteLine("[{0}] {1}", Thread.CurrentThread.ManagedThreadId, message);
    }
    

    DoAsTask输出:

    [1] Program Begin
    [1] 1 - Starting
    [1] 2 - Task started
    [3] A - Started something
    [3] B - Completed something
    [1] 3 - Task completed with result: 123
    [1] Program End
    

    DoAsAsync输出:

    [1] Program Begin
    [1] 1 - Starting
    [1] 2 - Task started
    [3] A - Started something
    [1] Program End
    [3] B - Completed something
    [3] 3 - Task completed with result: 123
    

    更新:通过在输出中显示线程ID来改进示例 .

  • 2

    Wait(),将导致以同步方式运行潜在的异步代码 . 等待不会 .

    例如,您有一个asp.net Web应用程序 . UserA调用/ getUser / 1 endpoints . asp.net app pool将从线程池(Thread1)中选择一个线程,并且该线程将进行http调用 . 如果你执行Wait(),这个线程将被阻塞,直到http调用解析 . 在等待时,如果UserB调用/ getUser / 2,那么app pool将需要服务另一个线程(Thread2)再次进行http调用 . 你刚刚创建了(好吧,实际从app池中获取)另一个线程,因为你无法使用Thread1它被Wait()阻止了 .

    如果在Thread1上使用await,则SyncContext将管理Thread1和http调用之间的同步 . 简单地说,一旦http呼叫完成,它将通知 . 同时,如果UserB调用/ getUser / 2,那么,你将再次使用Thread1进行http调用,因为它一旦被等待就被释放了 . 然后另一个请求可以使用它,甚至更多 . 一旦http调用完成(user1或user2),Thread1就可以获得结果并返回调用者(客户端) . Thread1用于多个任务 .

  • 366

    在这个例子中,实际上并不多 . 如果您正在等待在不同线程上返回的任务(如WCF调用)或放弃对操作系统的控制(如文件IO),则await将通过不阻塞线程来使用较少的系统资源 .

  • 7

    在上面的示例中,您可以使用“TaskCreationOptions.HideScheduler”,并大大修改“DoAsTask”方法 . 该方法本身不是异步的,因为它与“DoAsAsync”一起发生,因为它返回“任务”值并标记为“异步”,进行了几种组合,这就是它给我与使用“async / await”完全相同的方式:

    static Task DoAsTask()
    {
        WriteOutput("1 - Starting");
        var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime, TaskCreationOptions.HideScheduler); //<-- HideScheduler do the magic
    
        TaskCompletionSource<int> tsc = new TaskCompletionSource<int>();
        t.ContinueWith(tsk => tsc.TrySetResult(tsk.Result)); //<-- Set the result to the created Task
    
        WriteOutput("2 - Task started");
    
        tsc.Task.ContinueWith(tsk => WriteOutput("3 - Task completed with result: " + tsk.Result)); //<-- Complete the Task
        return tsc.Task;
    }
    

相关问题