【问题标题】:What is a rigorous definition of what should be an "awaitable" task?什么是“可等待”任务的严格定义是什么?
【发布时间】:2016-12-10 00:17:45
【问题描述】:

我试图深入了解async-await。每当我看到它如何工作的示例时,该示例都会使用一个名为 LongRunningTask 或类似名称的 awaitable 方法,其中是 Task.Delaynew WebClient().Download("http://google.com") 或类似名称。我试图弄清楚什么是应该等待的严格定义。它不能简单地是“长期运行的任务”,因为长期运行的任务可能类似于查找大小为 1,000,000,000 的数组的最大子数组,这不会给awaited on 带来任何性能优势如果async-await 确实没有创建任何新线程。

据我了解,如果你有类似的东西

var task = LongRunningTask();
DoSomething();
int x = DoSomethingElse();
int y = await task;
int z = x + y;

然后是块

DoSomething();
int x = DoSomethingElse();

是你向编译器暗示的

“嘿,编译器,如果你能验证这个块没有任何 依赖于LongRunningTask,反之亦然,然后你可以开始 在LongRunningTask 和这段代码之间划分你的工作。 在你完成这个的时候你还没有完成LongRunningTask 代码块,然后就完成LongRunningTask,因此 为什么我告诉你await它。”

你能帮我弄清楚吗?

【问题讨论】:

  • Async/await 是在 4.5 中添加的,目的是让面向 WinRT(又名 UWP,又名 Modern UI,又名 Store,又名 Phone)的程序员有机会编写正确的代码。它有一个非常严格的定义,任何可能需要超过 50 毫秒的时间。您可能比 anything 更具选择性,50 毫秒相当严格,我个人使用 1 秒作为“太长”的限制,如果合理的话,会加入沙漏光标。避免耗尽小电池是一个完全不同的考虑,然后它变得严厉。

标签: c# .net asynchronous async-await task-parallel-library


【解决方案1】:

await 没有你想象的那么神奇。在您的示例中,是否使用其他线程或 I/O 完成例程等的决定不在您显示的代码中任何

LongRunningTask 的实现作为一个返回已经启动的Task 的方法,已经做出了关于Task如何何时 的任何复杂决定完成。

await 所做的只是等待(顾名思义)已经已经开始完成的事情。

现在,if LongRunningTask 是通过创建新任务来实现的,并且这些任务正在使用使用该线程池的默认调度程序,并且如果您当前的方法是已经在线程池线程上运行并且LongRunningTask 创建的一些任务正在等待线程池线程可用,那么可能当遇到await 时,运行您的代码的线程变为可用,并将用于开始运行其中一个等待任务。但这主要是您不需要考虑的事情。

【讨论】:

    【解决方案2】:

    就您的问题而言,长期运行的任务是任何涉及输入/输出 (I/O) 的任务。

    这是可以等待的最优秀的任务类别,因为 I/O 通常会发生计算机发出指令以开始 I/O,然后只是等待(通常处于低 CPU 状态)完成 I/O。

    所以,new WebClient().Download("http://google.com") 是涉及 I/O 的任务的一个很好的例子,因为为了执行这个任务,机器需要准备一个到 google.com 的请求,开始通过套接字发送它,等待请求完全发送,然后等待响应开始到达,然后等待响应完全到达。

    但是,这个定义并不严格;即使找到大小为 1,000,000,000 的数组的最大子数组也可能是一个长时间运行的任务,如果它是通过产生一个新线程来实现的,因为就调用线程而言,我们将有一个非常相似的场景:我们设置-启动操作,(生成新线程,将要搜索的数组传递给它),然后等待结果,什么也不做。

    编辑

    鉴于上述情况,最大子数组的例子可以被认为是一个红鲱鱼,因为对于异步等待的目的,它是否是一个问题长时间运行的任务取决于它是否在单独的线程中启动。

    因此,对长时间运行的任务的更好定义可能是:

    任何需要花费大量时间才能完成的任务 不希望在当前执行的线程中阻塞等待。

    因此,例如,如果您正在编写一个交互式 (GUI) 应用程序,并且您想保证对您的人类用户的响应时间为 200 毫秒,那么任何需要花费比这更长的时间的任务都是长时间运行的任务:您启动它,等待它,只要它在运行,您的 GUI 仍然是响应式的。另一方面,如果您正在编写一个批处理作业,它打算在一夜之间运行以处理一些大数据,那么几乎没有任何任务值得被视为长时间运行的任务。

    【讨论】:

    • Task.Delay 是否涉及 I/O?
    • 不,它没有。它只是设置了一个稍后将触发的计时器,然后立即返回。当计时器触发时,等待完成。但他们只在示例中将其用作真正长时间运行的任务的 占位符
    【解决方案3】:

    如上述答案所述,任务可以启动不同的线程并等待该特定任务的结果。

    通常像 API 调用这样的任务完全独立于处理能力,即即使您有最大的 RAM,CPU 对您正在寻求的响应也没有影响或影响很小,即 IO 绑定延迟是需要完成的任务。而这些任务是由工作线程在内部完成的,通常应该等待

    参考:Essential C# 6.0(第 5 版)

    【讨论】:

    • 1) 如果您想引用另一个答案,通常最好包含指向它的链接而不是使用位置短语(例如“以上”),因为答案可能会因投票而重新排列,随机化等 2) await not 启动线程。 await 不会开始 任何东西。 await 总是与“awaitables”一起工作,而那些可等待的项目(例如任务)代表已经已经由其他东西开始的工作。
    • 是的——这里的重要区别是await 不知道也不关心底层进程是否使用线程;它只关心它现在应该返回一个不完整的任务并在以后继续。 一般而言(即在大多数用例中)可能没有线程,但这仅仅是因为大多数用例本质上是异步任务,例如发出 HTTP 请求。
    猜你喜欢
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-26
    • 1970-01-01
    • 2012-12-26
    • 2017-05-01
    相关资源
    最近更新 更多