简体   繁体   English

为什么异步等待无法正常工作

[英]Why the async await not working as expected

I am learning TPL (async/await) from a tutorial and I tried to test it out myself using a console application. 我正在从教程中学习TPL(异步/等待),并尝试使用控制台应用程序对其进行测试。 Please don't be offended by my ignorance. 请不要被我的无知所冒犯。 I am sure I am doing wrong somewhere- I wrote the following code: 我确定我在某处做错了-我编写了以下代码:

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

It's plain C# console code. 这是纯C#控制台代码。 My question is why do I get following output: 我的问题是为什么我得到以下输出:

3/5/2015 5:22:37 PM: Waiting started...
3/5/2015 5:22:37 PM: Waiting ended...
26

Shouldn't there be considerable time gap between"Waiting started" and "Waiting Ended"? “等待开始”和“等待结束”之间是否应该有相当长的时间间隔?

UPDATE 更新

After the answers, I figured out that Task.Delay(..) and Thread.Sleep(..) aren't same because they work very differently. 在回答之后,我发现Task.Delay(..)和Thread.Sleep(..)是不一样的,因为它们的工作方式非常不同。 Here is a nice link that explains with an example. 这是一个很好的链接 ,并举例说明。

Seems it would be a mistake to consider TPL as yet another framework for multithreading. 似乎将TPL视为另一种多线程框架是错误的。 All the answers helped me , so I voted for all. 所有的答案对我都有帮助,所以我投了赞成票。 However, I select Jon's answer as it is the most illustrative and the first one to appear. 但是,我选择了乔恩的答案,因为这是最具说明性的,也是第一个出现的答案。 Thanks all! 谢谢大家!

No, because in fact you're sleeping for 7 seconds before you print Waiting started . 否,因为实际上您在打印“ Waiting started之前要睡眠7秒钟。 Here's what happens: 这是发生了什么:

  • Main calls ProcessNumber 主要电话ProcessNumber
  • ProcessNumber sleeps for 1 second ProcessNumber睡眠1秒
  • ProcessNumber calls IncrementNumber ProcessNumber调用IncrementNumber
  • IncrementNumber sleeps for 6 seconds IncrementNumber睡眠6秒
  • IncrementNumber performs a computation, then returns a completed task IncrementNumber执行计算,然后返回完成的任务
  • ProcessNumber awaits that completed task, and continues ProcessNumber等待完成的任务,然后继续
  • ProcessNumber returns a completed task ProcessNumber返回完成的任务
  • Main prints "Waiting started" 主版画“等待开始”
  • Main waits for the already-completed task to complete (no-op) Main等待已完成的任务完成(无操作)
  • Main prints "Waiting ended" 主版画“等待中”

If you change each Thread.Sleep to 如果将每个Thread.Sleep更改为

await Task.Delay(...);

then you'll see what you expect. 然后您将看到您的期望。 The new flow will be: 新流程将是:

  • Main calls ProcessNumber 主要电话ProcessNumber
  • ProcessNumber calls Task.Delay and awaits the result... ProcessNumber调用Task.Delay并等待结果...
  • ... and because it hasn't completed yet, ProcessNumber returns an ongoing task to Main ...并且由于尚未完成,ProcessNumber将正在进行的任务返回给Main
  • Main prints "Waiting started" 主版画“等待开始”
  • Main waits for the task returned by ProcessNumber to complete Main等待ProcessNumber返回的任务完成
  • The Task.Delay task completes Task.Delay任务完成
  • The continuation in ProcessNumber starts to execute... ProcessNumber中的继续操作开始执行...
  • ProcessNumber calls IncrementNumber ProcessNumber调用IncrementNumber
  • IncrementNumber calls Task.Delay and awaits the result... IncrementNumber调用Task.Delay并等待结果...
  • ... and because it hasn't completed yet, IncrementNumber returns an ongoing task to ProcessNumber ...并且由于尚未完成,因此IncrementNumber将正在进行的任务返回给ProcessNumber
  • ProcessNumber awaits the task, which hasn't completed, so the continuation exits. ProcessNumber等待尚未完成的任务,因此继续执行。
  • The second delay task completes 第二个延迟任务完成
  • The continuation within IncrementNumber executes IncrementNumber中的延续执行
  • It reaches the end of IncrementNumber, and sets the result of the associated task 它到达IncrementNumber的末尾,并设置关联任务的结果
  • The continuation in ProcessNumber (which was awaiting that task) now executes 现在执行ProcessNumber(正在等待该任务)的继续
  • It reaches the end of ProcessNumber, and sets the result of the associated task 它到达ProcessNumber的末尾,并设置关联任务的结果
  • The call to Task.Wait in Main completes Main中对Task.Wait的调用完成
  • Main prints "Waiting ended" etc 主版画“等待中”等

It's really important to understand that until an async method hits the first await where it actually needs to pause (because the thing it's awaiting hasn't completed), it executes synchronously. 真正重要的是要理解,直到异步方法在实际上需要暂停的第一个await发生(因为等待状态尚未完成)之前,它才同步执行。 It's not like calling an async method splits off into a different thread. 这不像调用异步方法将其拆分为另一个线程一样。

It is because none of your methods are asynchronous. 这是因为您的方法都不是异步的。 They pretend to be asynchronous by returning Task but they all execute synchronously. 它们通过返回Task来伪装成异步的,但是它们都是同步执行的。

In fact you should be getting the compiler warning for IncrementNumber number saying This async method lacks 'await' operators and will run synchronously... etc . 实际上,您应该收到针对IncrementNumber号的编译器警告,说明此异步方法缺少'await'运算符,并且将同步运行...等 It basically says that your method will be executed synchronously. 它基本上说您的方法将同步执行。 This answer explains it in detail . 这个答案详细解释了

So when ProcessNumber returns the whole nested methods calls are already finished synchronously. 因此,当ProcessNumber返回时,整个嵌套方法调用已经同步完成。

Change the method to use await Task.Delay instead of Thread.Sleep to make it asynchronous. 更改方法以使用await Task.Delay而不是Thread.Sleep使其异步。

you need to refactor your code. 您需要重构代码。 like this 像这样

    static async Task<int> IncrementNumber(int j_)
{
    await Task.Delay(...);
    return (j_ + 23) / 23;
}
  1. a function with keyword async must have at least one await inside the body in order to run function async. 具有关键字async的函数在体内必须至少等待一个等待时间,以便运行函数async。
  2. Thread.Sleep(...) is also responsible for this. Thread.Sleep(...)也对此负责。
  3. if you found CPU bound task instead of Thread.Sleep then you need to do something like this. 如果找到CPU绑定任务而不是Thread.Sleep,则需要执行以下操作。

     //it will create a new thread then sleep. await Task.Run( () => ( CPUBoundTask()); var integer = await IncrementNumber(i_ * 23); return integer; 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM