async-await如何在使用事件循环的应用程序(如Windows窗体)中实现响应?

use*_*000 -4 .net c# asynchronous winforms async-await

据我所知,UI应用程序有一个主线程,其循环等效于

while(true)
{
   var e = events.Dequeue(); // get event from the queue
   e(); // run event
}
Run Code Online (Sandbox Code Playgroud)

我感到困惑的e()将是"畅通",如果它是在顶部async- await链.让我们说e()最终要求

public async Task WaitOneSecondAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done waiting 1 second!");
}
Run Code Online (Sandbox Code Playgroud)

一旦主线程命中await Task.Delay(1000);它就不会"继续"到while循环的下一次迭代.所以你能解释一下这是如何解锁的吗?也许有代码示例?

Eri*_*ert 6

一旦主线程命中等待Task.Delay(1000); 它不会"继续"到while循环的下一次迭代

是的,它会的.

请记住,Task.Delay不是Thread.Sleep.睡眠会关闭你的线程,直到它再次醒来才会返回.Task.Delay 立即返回表示延迟的任务.

所以你能解释一下这是如何解锁的吗?

await 有以下行为:

  • 检查任务以查看是否完成.如果是,并且它以异常完成,则抛出异常.如果是,并且它正常完成,则生成值(如果有),并继续正常执行.
  • 任务尚未完成.创建一个委托,其行动是在等待点再次开始运行此方法.(这对于编译器来说是一个棘手的问题;请参阅有关await codegen详细信息的任何文章以获得解释.)将该委托作为任务的继续进行注册,然后返回给调用者.
  • 如果这是方法中遇到的第一个 await,则返回的东西是表示方法工作流的任务,以便调用者可以依次等待它.

public async Task WaitOneSecondAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done waiting 1 second!");
}
Run Code Online (Sandbox Code Playgroud)

有这些伪代码语义:

    Task thisTask = a new task;
    Action completion = () => { 
      Console.WriteLine("Done..."); 
      mark thisTask as complete
    };
    Task delayTask = Task.Delay(1000);
    if (delayTask has already completed normally)
    {
        completion();
        return thisTask; // Return a completed task.
    }
    else if (delayTask completed with an exception)
        throw the exception
    else
        assign completion as the completion of delayTask
        return thisTask;
Run Code Online (Sandbox Code Playgroud)

看看它是如何工作的?如果延迟尚未完成,则该方法在完成延迟时注册一个代理并返回给调用者.如果调用者是消息循环,则消息循环将再次运行.

延迟完成后会发生什么? 它将一条消息称为完成,并在未来的某个时间点完成.

当然真正的codegen 比我在这里展示的小伪代码要复杂得多; 这只是为了让这个想法得以实现.真正的codegen很复杂,因为异常处理比抛出异常更困难,并且在可能的情况下通过各种优化延迟在堆上生成对象.