如果 async-await 不创建任何额外的线程,那么它如何使应用程序响应?

2024-05-20

一次又一次,我看到它说使用async-await不创建任何额外的线程。这是没有意义的,因为计算机看起来一次做不止一件事的唯一方式是

  • 实际上一次做不止一件事(并行执行,利用多个处理器)
  • 通过调度任务并在它们之间切换来模拟它(做一点A,一点B,一点A,等等)

So if async-await两者都没有,那么它如何使应用程序具有响应能力呢?如果只有 1 个线程,那么调用任何方法都意味着在执行其他操作之前等待该方法完成,并且该方法内部的方法必须等待结果才能继续,依此类推。


事实上,async/await 并没有那么神奇。完整的主题相当广泛,但为了快速而完整地回答您的问题,我认为我们可以做到。

让我们处理 Windows 窗体应用程序中的简单按钮单击事件:

public async void button1_Click(object sender, EventArgs e)
{
    Console.WriteLine("before awaiting");
    await GetSomethingAsync();
    Console.WriteLine("after awaiting");
}

我要去明确地 not谈论无论是什么GetSomethingAsync现在正在返回。假设这将在 2 秒后完成。

在传统的非异步世界中,按钮单击事件处理程序将如下所示:

public void button1_Click(object sender, EventArgs e)
{
    Console.WriteLine("before waiting");
    DoSomethingThatTakes2Seconds();
    Console.WriteLine("after waiting");
}

当您单击表单中的按钮时,应用程序将冻结大约 2 秒,同时我们等待此方法完成。所发生的情况是“消息泵”(基本上是一个循环)被阻塞。

这个循环不断地询问窗口“有没有人做了什么事情,比如移动鼠标、点击什么东西?我需要重新绘制一些东西吗?如果是这样,请告诉我!”然后处理那个“东西”。该循环收到用户单击“button1”的消息(或来自 Windows 的等效类型的消息),并最终调用我们的button1_Click方法同上。在该方法返回之前,该循环现在一直处于等待状态。这需要 2 秒,在此期间不会处理任何消息。

大多数处理窗口的事情都是使用消息完成的,这意味着如果消息循环停止发送消息,即使只是一秒钟,用户也会很快注意到。例如,如果您将记事本或任何其他程序移到您自己的程序之上,然后再次移开,则会向您的程序发送一系列绘画消息,指示窗口的哪个区域现在突然再次变得可见。如果处理这些消息的消息循环正在等待某些内容并被阻塞,则不会完成任何绘制。

所以,如果在第一个例子中,async/await不创建新线程,它是如何做到的?

好吧,发生的情况是你的方法被分成了两个。这是广泛主题类型的事物之一,因此我不会过多讨论细节,但足以说明该方法分为以下两部分:

  1. 导致的所有代码await,包括调用GetSomethingAsync
  2. 全部代码如下await

插图:

code... code... code... await X(); ... code... code... code...

重新排列:

code... code... code... var x = X(); await X; code... code... code...
^                                  ^          ^                     ^
+---- portion 1 -------------------+          +---- portion 2 ------+

基本上该方法执行如下:

  1. 它执行一切直到await

  2. 它称为GetSomethingAsync方法,它做它的事情并返回未来 2 秒内完成的事情

    到目前为止,我们仍然处于对 Button1_Click 的原始调用中,该调用发生在主线程上,从消息循环调用。如果代码导致await花费很多时间,UI 仍然会冻结。在我们的例子中,没有那么多

  3. 什么是await关键字,加上一些聪明的编译器魔法,所做的就是它基本上类似于“好吧,你知道吗,我将简单地从此处的按钮单击事件处理程序返回。当您(例如,我们正在等待的事情) for) 抽出时间来完成,请告诉我,因为我还有一些代码需要执行”。

    实际上它会让SynchronizationContext 类 https://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext(v=vs.110).aspx知道它已经完成,根据当前正在运行的实际同步上下文,它将排队等待执行。 Windows 窗体程序中使用的上下文类将使用消息循环泵送的队列对其进行排队。

  4. 因此它返回到消息循环,该循环现在可以自由地继续发送消息,例如移动窗口、调整窗口大小或单击其他按钮。

    对于用户来说,UI 现在再次响应,处理其他按钮点击、调整大小,最重要的是,重画,所以它似乎没有冻结。

  5. 2 秒后,我们正在等待的事情完成,现在发生的事情是它(好吧,同步上下文)将一条消息放入消息循环正在查看的队列中,说“嘿,我还有一些代码你去执行”,这段代码就是全部代码after等待。

  6. 当消息循环到达该消息时,它基本上会在它离开的地方“重新进入”该方法,紧接着await并继续执行该方法的其余部分。请注意,此代码再次从消息循环中调用,因此如果此代码碰巧在不使用async/await正确的话,它会再次阻塞消息循环

这里有很多活动部件,所以这里有一些更多信息的链接,我想说“如果你需要它”,但是这个主题is相当广泛,了解这一点相当重要其中一些活动部件。您总是会明白 async/await 仍然是一个有漏洞的概念。一些潜在的限制和问题仍然会泄漏到周围的代码中,如果没有泄漏,您通常最终不得不调试一个看似没有充分理由随机中断的应用程序。

  • 使用 Async 和 Await 进行异步编程(C# 和 Visual Basic) https://msdn.microsoft.com/en-us/library/hh191443.aspx
  • SynchronizationContext 类 https://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext(v=vs.110).aspx
  • 斯蒂芬·克利里 - 没有线索 http://blog.stephencleary.com/2013/11/there-is-no-thread.html 非常值得一读!
  • Channel 9 - Mads Torgersen:C# 异步内部 https://sec.ch9.ms/ch9/f586/30f29955-f1f7-4a25-a790-152d536df586/Mads-Torgersen-Inside-C-Async_mid.mp4 非常值得一看!

好吧,那如果GetSomethingAsync启动一个将在 2 秒内完成的线程?是的,那么显然有一个新的线程正在发挥作用。然而,该线程不是because这个方法的异步性,是因为这个方法的程序员选择了一个线程来实现异步代码。几乎所有异步 I/Odon't使用线程,他们使用不同的东西。async/await 通过他们自己不启动新线程,但显然“我们等待的事情”可以使用线程来实现。

.NET 中有很多东西不一定会自己启动线程,但仍然是异步的:

  • Web 请求(以及许多其他与网络相关的需要时间的事情)
  • 异步文件读写
  • 还有更多,一个好兆头是所讨论的类/接口是否具有名为的方法SomethingSomethingAsync or BeginSomething and EndSomething还有一个IAsyncResult涉及。

通常这些东西在底层不使用线程。


好的,那么您想要一些“广泛的主题内容”吗?

好吧,我们问一下尝试罗斯林 http://tryroslyn.azurewebsites.net/关于我们的按钮点击:

尝试罗斯林 http://tryroslyn.azurewebsites.net/#K4Zwlgdg5gBAygTxAFwKYFsDcAoUlaIoYB0AKgBYBOqAhgCb5k0gDWIO2ADsAEYA2YAMYxBfZiBgBhGAG9sMBTG78hMAG4B7MHRgBZABQBKWfMUBfUwstLeA4cwQRhm7TB7BkyDRACMAfUk7Fn0NHgArVEFkGBBUCDpUSgAaGABRNTjkAEFKKAlUQ2s5RRKpbxANPlRiAHVKMDQAGUhUfQAiHlQAMw1qGBoAdxoG/DbDHFLFQeHogHFUZDgNdAXyfCyQR0EjCcmyiAqq2vqmlvaaLrRKfqGR6DHdhQsS62U7GFJmFhh5xeXV9abJxGIrWErUZDASgQD5fYgAEVQYgQ+gATAAGTHjawWMxAA=

我不会在这里链接完整生成的类,但它是非常血腥的东西。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果 async-await 不创建任何额外的线程,那么它如何使应用程序响应? 的相关文章

  • 检测wlan是否关闭

    任何人都可以给我一个提示 如何在 Windows Phone 上以编程方式检测 C 8 1 应用程序 不是 8 0 是否启用 禁用 WLAN 我不想更改这些设置 只是需要知道 该解决方案是一个 Windows 8 1 通用应用程序 Wind
  • 在现代 C++ 中,临时生命周期延长何时有用?

    在 C 中 您可以将函数的返回值 返回值 而不是引用 绑定到 const 引用 并且代码仍然有效 因为该临时对象的生命周期将延长到作用域末尾 例如 std string get string return abc void f const
  • 将完整模板参数值映射到原始类型

    我想将数字映射到类型 在这个例子中 我将创建一个函数 将 sizeof 结果映射到有符号的原始类型 我想知道是否有更好的方法来完成我在现代 C 中所做的事情 即采用模板化值并将其转换为类型 现在 这可以将大小转换为已知类型 但我似乎无法在标
  • 解析 JWT 令牌以仅获取有效负载内容,无需 C# 或 Blazor 中的外部库

    我正在使用 Blazor 编写可以访问 JWT 的客户端应用程序 我想知道一种简单的方法来读取令牌有效负载内容而不添加额外的依赖项 因为我不需要其他信息 也不需要验证令牌 我认为解析有效负载内容应该足够简单 只需将其写入方法即可 JwtTo
  • 在 C# 中调用 C++ 库 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有很多用 C 编写的库 我想从 C 调用这些库 但是 我遇到了很多问题 我想知道是否有书籍或指南告诉我如何做到这一点 Dll导入 htt
  • C# 开源 NMEA 解析器 [已关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找 C 开源 NMEA 解析器 嗯 我自己也不熟悉 但是一些快速搜索显示了一个代码项目 htt
  • 获取 boost Spirit 语法中的当前行

    我正在尝试使用 boostspirit 获取正在解析的文件的当前行 我创建了一个语法类和结构来解析我的命令 我还想跟踪在哪一行找到命令并将其解析到我的结构中 我将 istream 文件迭代器包装在 multi pass 迭代器中 然后将其包
  • 访问 ascx 文件中的母版页控件

    我有一个母版页文件 其中包含 2 个面板控件中的 2 个菜单 我还使用控件来检查用户是否登录并获取用户类型 根据我想要显示 隐藏面板的类型 控件本身不在母版页中引用 而是通过 CMS 系统动态引用 我想在用户控件中使用findcontrol
  • 等待异步 grunt 任务完成

    我收到了 grunt 设置 其中一个新任务应该执行 grunt task run 已经存在的任务 要执行的任务是异步的 新任务应该等待异步任务完成 执行此操作的首选方法是什么 grunt 已经涵盖了这一点 你应该将你的任务声明为异步任务 并
  • C#6 中的长字符串插值行

    我发现 虽然字符串插值在应用于现有代码库的字符串 Format 调用时非常好 但考虑到通常首选的列限制 字符串对于单行来说很快就会变得太长 特别是当被插值的表达式很复杂时 使用格式字符串 您将获得一个可以拆分为多行的变量列表 var str
  • 使用 GCC 生成可读的程序集?

    我想知道如何使用GCC http en wikipedia org wiki GNU Compiler Collection在我的 C 源文件中转储机器代码的助记符版本 这样我就可以看到我的代码被编译成什么 你可以使用 Java 来做到这一
  • Java 中处理异步响应的设计模式

    我读过类似问答的答案 如何在 JAVA 中创建异步 HTTP 请求 https stackoverflow com questions 3142915 how do you create an asynchronous http reque
  • 在 OpenGL 中渲染纹理 1 到 1

    所以我想做的是使用 OpenGL 和 C 将纹理渲染到平面上 作为显示图像的一种方式 但是我需要确保在渲染纹理时没有对纹理进行任何处理 抗锯齿 插值 平滑 模糊等 这是 OpenGL 处理渲染纹理的默认方式吗 或者是否需要设置一些标志才能禁
  • ASP.NET MVC 路由:如何从 URL 中省略“索引”

    我有一个名为 StuffController 的控制器 具有无参数索引操作 我希望从表单中的 URL 调用此操作mysite com stuff 我的控制器定义为 public class StuffController BaseContr
  • 在 C#.NET 中安全删除文件

    在我正在做的一个项目中 我想为用户提供 安全 删除文件的选项 例如 用随机位或 0 覆盖它 在 C NET 中是否有一种简单的方法可以做到这一点 效果如何 你可以调用系统内部删除 http technet microsoft com en
  • C++ 中 void(*)() 和 void(&)() 之间的区别[重复]

    这个问题在这里已经有答案了 在此示例代码中 func1是类型void int double and funky是类型void int double include
  • LINQ 中的“from..where”或“FirstOrDefault”

    传统上 当我尝试从数据库中获取用户的数据时 我使用了以下方法 在某种程度上 DbUsers curUser context DbUsers FirstOrDefault x gt x u LoginName id string name c
  • 如何得知客户端从服务器的下载速度?

    根据客户的下载速度 我想以低质量或高质量显示视频 任何 Javascript 或 C 解决方案都是可以接受的 Thanks 没有任何办法可以确定 您只能测量向客户端发送数据的速度 如果没有来自客户端的任何类型的输入来表明其获取信息的速度 您
  • INotifyPropertyChanged 和 propertyName

    我一直不确定它的含义propertyName实施时INotifyPropertyChanged 所以一般来说你实现INotifyPropertyChanged as public class Data INotifyPropertyChan
  • 使用未分配的局部变量

    我遇到了一个错误 尽管声明了变量 failturetext 和 userName 错误仍然出现 谁能帮帮我吗 Use of Unassigned local variable FailureText Use of Unassigned lo

随机推荐