我可能遗漏了一些东西,但是做这件事有什么区别:
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);
}
我可能遗漏了一些东西
你是。
Task.Wait 和 await 任务有什么区别?
您从餐厅的服务员那里订购午餐。在您下订单后不久,一位朋友走进来并坐在您旁边并开始交谈。现在你有两个选择。你可以忽略你的朋友,直到任务完成——你可以等到汤上来,在等待的时候什么也不做。或者你可以回应你的朋友,当你的朋友停止说话时,服务员会给你端上你的汤。
Task.Wait
阻塞直到任务完成 - 在任务完成之前您忽略您的朋友。 await
继续处理消息队列中的消息,当任务完成时,它会将一条消息排入队列,上面写着“在等待之后从你离开的地方继续”。你和你的朋友交谈,当谈话中断时,汤就来了。
为了演示埃里克的答案,这里有一些代码:
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);
}
//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!
”具有误导性。在按钮单击事件处理程序 ButtonClick()
中按下带有 t.Wait();
的按钮后,无法按下任何内容,然后在控制台中看到某些内容并更新标签“直到此任务完成”,因为 GUI 已冻结且无响应,即任何与 GUI 的点击或交互都将丢失,直到任务等待完成
t.Wait
将阻塞主线程,直到任务完成。”
这个例子非常清楚地说明了差异。使用 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 来改进示例。
Wait(),将导致以同步方式运行潜在的异步代码。 await 不会。
例如,您有一个 asp.net Web 应用程序。 UserA 调用 /getUser/1 端点。 asp.net 应用程序池将从线程池 (Thread1) 中选择一个线程,并且该线程将进行 http 调用。如果你执行 Wait(),这个线程将被阻塞,直到 http 调用解决。在等待期间,如果 UserB 调用 /getUser/2,则应用程序池将需要服务另一个线程 (Thread2) 以再次进行 http 调用。您刚刚无缘无故地创建了(实际上是从应用程序池中获取的)另一个线程,因为您不能使用 Thread1 它被 Wait() 阻塞。
如果您在 Thread1 上使用 await,则 SyncContext 将管理 Thread1 和 http 调用之间的同步。简单地说,一旦 http 调用完成,它就会通知。同时,如果 UserB 调用 /getUser/2,那么,你将再次使用 Thread1 进行 http 调用,因为一旦 await 被命中,它就会被释放。然后另一个请求可以使用它,甚至更多。一旦 http 调用完成(user1 或 user2),Thread1 可以获取结果并返回给调用者(客户端)。 Thread1 用于多个任务。
在这个例子中,实际上并不多。如果您正在等待在不同线程上返回的任务(如 WCF 调用)或将控制权交给操作系统(如文件 IO),则 await 将通过不阻塞线程来使用更少的系统资源。
在上面的例子中,可以使用“TaskCreationOptions.HideScheduler”,并对“DoAsTask”方法进行大幅度修改。该方法本身不是异步的,因为它与“DoAsAsync”一起发生,因为它返回一个“Task”值并标记为“async”,进行多种组合,这就是它给我的方式与使用“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;
}
Task
实际上会在您的线程上执行一个 10 小时长的Task
,从而阻塞您整个 10 小时,您会怎么想?