C# 为什么异步等待未按预期工作
我正在从教程中学习TPL(async/await),并尝试使用控制台应用程序对其进行测试。请不要因为我的无知而生气。 我确信我在某个地方做错了-我编写了以下代码:C# 为什么异步等待未按预期工作,c#,task-parallel-library,async-await,.net-4.5,c#-5.0,C#,Task Parallel Library,Async Await,.net 4.5,C# 5.0,我正在从教程中学习TPL(async/await),并尝试使用控制台应用程序对其进行测试。请不要因为我的无知而生气。 我确信我在某个地方做错了-我编写了以下代码: static void Main(string[] args_) { Task<int> task = ProcessNumber(25); Console.WriteLine(string.Format("{0}: Waiting started...", DateTime.Now)); task
static void Main(string[] args_)
{
Task<int> task = ProcessNumber(25);
Console.WriteLine(string.Format("{0}: Waiting started...", DateTime.Now));
task.Wait();
Console.WriteLine(string.Format("{0}: Waiting ended...", DateTime.Now));
Console.WriteLine(task.Result);
Console.WriteLine("Press any key to terminate!");
Console.ReadLine();
}
static async Task<int> ProcessNumber(int i_)
{
Thread.Sleep(1000);
var integer = await IncrementNumber(i_ * 23);
return integer;
}
static async Task<int> IncrementNumber(int j_)
{
Thread.Sleep(6000);
return (j_ + 23) / 23;
}
“等待开始”和“等待结束”之间不应该有相当大的时间间隔吗
更新
在回答之后,我发现Task.Delay(..)和Thread.Sleep(..)不一样,因为它们的工作方式非常不同。
下面是一个很好的例子
似乎把TPL看作是多线程的另一个框架是错误的。所有的答案都对我有帮助,所以我投了所有人的票。然而,我选择乔恩的答案,因为它是最具说明性的,也是第一个出现的答案。
谢谢大家 这是因为您的方法都不是异步的。它们通过返回
Task
假装异步,但它们都是同步执行的
事实上,您应该会收到编译器警告,指出此异步方法缺少'await'运算符,并且将同步运行。IncrementNumber
number。。。它基本上说你的方法是同步执行的
因此,当ProcessNumber
返回整个嵌套方法时,调用已经同步完成
将方法更改为使用
wait Task.Delay
而不是Thread.Sleep
使其异步。否,因为实际上在打印等待开始之前,您正在睡眠7秒钟。下面是发生的情况:
- 主叫号码
- ProcessNumber休眠1秒
- ProcessNumber调用IncrementNumber
- 递增数字睡眠6秒
- IncrementNumber执行计算,然后返回已完成的任务
- ProcessNumber等待完成的任务,并继续
- ProcessNumber返回已完成的任务
- 主打印“等待开始”
- Main等待已完成的任务完成(无操作)
- 主打印“等待结束”
如果将每个线程。Sleep
更改为
await Task.Delay(...);
然后你会看到你所期待的。新流程将是:
- 主叫号码
- ProcessNumber调用Task.Delay并等待结果
- 。。。由于尚未完成,ProcessNumber会将正在进行的任务返回给Main
- 主打印“等待开始”
- Main等待ProcessNumber返回的任务完成
- 任务。延迟任务完成
- ProcessNumber中的延续开始执行
- ProcessNumber调用IncrementNumber
- IncrementNumber调用任务。延迟并等待结果
- 。。。因为它还没有完成,IncrementNumber将一个正在进行的任务返回给ProcessNumber
- ProcessNumber等待尚未完成的任务,因此继续操作将退出
- 第二个延迟任务完成
- 执行IncrementNumber中的延续
- 它到达IncrementNumber的末尾,并设置关联任务的结果
- ProcessNumber中的continuation(正在等待该任务)现在执行
- 它到达ProcessNumber的末尾,并设置关联任务的结果
- 对Task.Wait in Main的调用已完成
- 主要印刷品“等待结束”等
理解这一点非常重要,在异步方法到达实际需要暂停的第一个await
位置之前(因为它等待的东西还没有完成),它是同步执行的。这不像调用异步方法分裂成不同的线程。您需要重构代码。像这样
static async Task<int> IncrementNumber(int j_)
{
await Task.Delay(...);
return (j_ + 23) / 23;
}
谢谢Jon的回答-等待任务。延迟(…)解决方案非常有效。除此之外,我还更改了Thread.Sleep代码:静态异步任务SleepForMilSec(int milSec_){Thread.Sleep(milSec_);现在我使用wait SleepForMilSec(..)而不是ProcessNumber和IncrementNumber方法中的直接Thread.Sleep调用。仍然只有6秒的延迟。这意味着:ProcessNumber方法中的第一个Wait调用将被阻止。有什么想法吗?编辑:顺便说一句,你在Pluralsight中关于async Wait的课程是我将遵循的下一个教程。:@James:yourSleepForMilSec
方法应该有一个警告,说明你有一个异步方法没有等待,因此它将同步执行。注意:)是的..我注意到了警告,我也更新了问题!谢谢Jon。这非常有用!始终注意编译器警告。对于这段代码,编译器将给你一个警告,告诉你到底出了什么错。
static async Task<int> IncrementNumber(int j_)
{
await Task.Delay(...);
return (j_ + 23) / 23;
}
//it will create a new thread then sleep.
await Task.Run( () => ( CPUBoundTask());
var integer = await IncrementNumber(i_ * 23);
return integer;