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

时间:2012-03-01 15:55:32

标签: c# task-parallel-library .net-4.5 async-await conceptual

我可能会遗漏一些东西但是做什么之间的区别是:

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 个答案:

答案 0 :(得分:383)

  

我可能遗漏了一些东西

你是。

  

执行Task.Waitawait task之间的区别是什么?

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

Task.Wait阻止任务完成 - 在任务完成之前,您将忽略您的朋友。 await继续处理消息队列中的消息,并且当任务完成时,它会将一条消息标记为“在等待之后从中断处继续”。你跟你的朋友说话,当谈话休息时,汤就到了。

答案 1 :(得分:116)

为了证明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);
}

答案 2 :(得分:43)

此示例非常清楚地说明了这一区别。使用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来改进示例。

答案 3 :(得分:9)

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

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

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

答案 4 :(得分:8)

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

答案 5 :(得分:3)

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

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;
}