|
|
1
143
这绝对不是这个模式的用途 . 等待会发生 不 将操作放在新线程上。确保是这样 非常 对你来说很清楚。 等待将剩余的工作安排为高延迟操作的继续。 等待会发生 不 将同步操作转换为异步并发操作。 Await使使用已经异步的模型的程序员能够编写类似于同步工作流的逻辑 .等待既不会创建也不会破坏异步;它 管理 现有异步。 创建新线程就像雇佣工人一样。当你等待一项任务时,你并不是在雇佣一名工人来完成这项任务。你在问“这项任务已经完成了吗?如果没有,完成后给我回电话,这样我就可以继续做依赖于这项任务的工作。与此同时,我要去这里做另一件事……” 如果你正在纳税,你发现你需要工作中的一个号码,而邮件还没有送达,你就没有 雇一个工人在邮箱旁等候 等候 .它是 异步等待结果 .
这是为了管理延迟。
有两种方式。首先,通过确保应用程序在高延迟操作的世界中保持响应。这种性能对于那些不希望应用程序挂起的用户来说很重要。其次,通过为开发人员提供工具来表示异步工作流中的数据依赖关系。通过不阻塞高延迟操作,可以释放系统资源来处理未阻塞的操作。 没有线程。并发是实现异步的机制;它不是唯一的一个。
与什么相比反应更灵敏?与不等待就调用这些方法相比?不,当然不是。与同步等待任务完成相比?当然,是的。 不不不。停止思考并行性。不需要任何并行。 这样想吧。你想做一个煎蛋三明治。您有以下任务:
三项任务。第三项任务取决于前两项的结果,但前两项任务并不相互依赖。因此,以下是一些工作流:
问题是,你可能在煮鸡蛋的时候把吐司放在烤面包机里。替代工作流:
你明白为什么异步工作流要高效得多吗?在等待高延迟操作完成时,您完成了大量工作。 但你没有雇佣鸡蛋厨师和烤面包厨师 我建议的工作流程是:
现在,将其与以下内容进行比较:
你看到这个工作流有什么不同吗?此工作流是:
此工作流效率较低 因为我们未能捕捉到吐司和鸡蛋任务具有高延迟和独立性的事实 。但它肯定比做更有效地利用资源 没有什么 当你在等鸡蛋煮的时候。 这整件事的要点是:线程非常昂贵,所以 不要 启动新线程。相当地 在执行高延迟操作时,将线程投入工作,从而更有效地利用线程 .等待 不 也许计算是在另一个线程上进行的,也许它在磁盘上被阻塞了,不管怎样。没关系。关键是,等待是为了 管理 异步,不是 创建
回到类比。你正在做鸡蛋三明治,鸡蛋和吐司正在烹饪,所以你开始阅读邮件。鸡蛋煮好后,你把邮件放在一边,把鸡蛋从火上拿开。然后你回到邮件。然后烤面包片做好了,你做三明治。三明治做好后,你读完邮件。 你是如何做到这一切的,没有雇佣员工,一个人读邮件,一个人煮鸡蛋,一个人烤面包,一个人做三明治? 你只用一个工人就完成了这一切。 你是怎么做到的?通过将任务分解为小部分,记下哪些部分必须按哪个顺序完成,然后 协同多任务处理 这些碎片。 如今,拥有大平面虚拟内存模型和多线程进程的孩子们认为这是一直以来的事情,但我的记忆可以追溯到Windows3时代,那时的Windows3没有这一切。如果你想两件事情“并行”发生,你就这样做了:把任务分成小部分,然后轮流执行。整个操作系统都基于这个概念。 现在,你可以看看这个类比,然后说“好吧,但有些工作,比如烤面包,是由机器完成的”,并且 那个 是并行性的源泉。当然,我不需要雇佣工人来烤面包,但我在硬件方面实现了并行。这是正确的思考方式。 硬件并行和线程并行是不同的 。当您向网络子系统发出异步请求以从数据库中查找记录时 无线程 如果您想更详细地解释硬件如何与操作系统协同工作以实现异步,请阅读“ There is no thread “斯蒂芬·克利里(Stephen Cleary)。 所以当你看到“异步”时,不要认为“并行”。如果存在以下情况,请考虑“高延迟操作拆分为小块” 许多的 如果这些操作的各个部分不相互依赖,则可以 协同交织 在一个线程上执行这些片段。 正如你所想象的那样 非常困难 为了编写控制流,你可以放弃你现在正在做的事情,去做其他事情,然后无缝地从你停止的地方开始。这就是为什么我们让编译器这样做!“等待”的意义在于,它可以通过将异步工作流描述为同步工作流来管理这些工作流。在任何地方,你都可以把这个任务放在一边,然后再回来,写上“等待”。编译器将负责将代码转换成许多小片段,每个小片段可以在异步工作流中调度。 更新:
没有区别。
什么时候
让我再说一遍:
所以是的,
和
都是一样的,都是一样
和
都是一样的。
认为
现在我们接受这个任务
现在我们回来了。线程继续执行它所做的任何操作。在某个时刻,鸡蛋已经准备好了,所以
同样,调用返回一个任务,我们:
检查任务是否完成。如果是,我们分配
等等
消除
另一个更新:
有
一些
这是一个很好的例子。但让我们回到这里。目的
查看工作流中所需顺序的最简单方法是检查 数据相关性 。由于await从已完成的任务中提取值,因此必须有一个await 在某处 在创建烤面包机任务和创建三明治之间。
这么说毫无意义
因为这不是所需的工作流。 因此,对您的问题的实际回答是:将等待推迟到实际需要价值的时候是一种很好的做法,因为这增加了有效安排工作的机会。但你可以走得太远;确保实现的工作流是您想要的工作流。 |
|
|
2
3
一般来说,这是因为一旦异步函数能更好地与其他异步函数配合使用,就开始失去异步的好处。因此,调用异步函数的函数最终都是异步的,它会在整个应用程序中传播,例如,如果您与数据存储进行异步交互,那么使用该功能的东西也会成为异步的。
资料来源: Async/Await - Best Practices in Asynchronous Programming |
|
3
1
这是一个演员模特世界,真的。。。 我的观点是,异步/等待只是修饰软件系统的一种方式,以避免不得不承认,实际上,许多系统(尤其是那些具有大量网络通信的系统)最好被视为参与者模型(或者更好的是,通信顺序过程)系统。 有了这两个因素,整个要点就是你要等待几件事情中的一件完成,当一件完成时采取必要的行动,然后再回到等待中。具体来说,您正在等待来自其他地方的消息,阅读它,并根据内容采取行动。在*nix中,等待通常通过调用epoll()或select()来完成。 使用await/async只是一种假装您的系统仍然是某种同步方法调用(因此很熟悉)的方式,同时很难有效地处理每次不一致地按相同顺序完成的事情。 然而,一旦你克服了不再调用方法而是简单地来回传递消息的想法,一切就变得很自然了。这在很大程度上是一件“请做这件事”,“当然,答案在这里”的事情,许多这样的互动相互交织。在循环的顶部用一个大的WaitForLotsOfThings()调用来结束它,仅仅是一个明确的确认,即您的程序将等待,直到它有事情要做,以响应与它通信的许多其他程序。 Windows如何使其变得困难 不幸的是,Windows使实现proactor系统变得非常困难(“如果您现在阅读该消息,您会得到它”)。Windows就是反应器(“你让我读的那个消息?现在已经被读了。”)。这是一个重要的区别。 对于前者,表示“停止收听另一个参与者”的消息(或者实际上是超时)很容易处理,您只需在下次等待时将该参与者从您要收听的列表中排除即可。
我在一定程度上吹毛求疵。Proactor在具有动态连接的系统中非常有用,Actor会掉入系统,然后再次掉出。如果你有固定数量的演员,并且有永远不会消失的通信链接,那么反应器就可以了。然而,考虑到反应器系统很容易在前置器平台上实现,但前置器系统不能在反应器平台上轻松实现(时间不会倒流),我觉得Window的方法特别令人恼火。 因此,不管怎样,异步/等待肯定仍在反应堆中。 撞击后爆震 这已经感染了许多其他图书馆。 C++的Boost asio也是reactor,即使在*nix上也是如此,很大程度上似乎是因为他们希望有一个Windows实现。 ZeroMQ是一个proactor框架,它在Windows上基于对select()的调用(在Windows中仅适用于套接字),在一定程度上受到了限制。 对于Windows上的cygwin系列POSIX运行时,它们必须通过每个文件描述符有一个线程来实现select()、epoll()等 投票 (是的, 投票 !!!!) 用于输入数据的底层套接字/串行端口/管道,以便重新创建POSIX的例程。Yeurk!是的!cygwin-dev邮件列表上的评论可以追溯到他们实现该部分的时候,这让人读起来很有趣。 演员不一定慢 值得注意的是,短语“传递消息”并不一定意味着到处传递副本——有很多参与者模型的公式,你只是在传递消息引用的所有权(例如,数据流,C#中任务并行库的一部分)。这使它更快。我还没有抽出时间来研究Dataflow库,但它并没有真正让Windows proactor突然出现。它并没有为您提供一个处理各种数据载体(如套接字、管道、队列等)的actor模型proactor系统。
所以,刚刚抨击了Windows和它的劣质反应堆架构,一个有趣的地方是Windows 10现在运行Linux二进制文件。我非常想知道,微软是如何实现select()、epoll()下面的系统调用的,因为它必须在套接字、串口、管道和POSIX(文件描述符)土地上的其他所有东西上运行,而Windows上的其他东西都不能运行?我真想知道那个问题的答案。 |
|
|
A B · C#Excel自动调整列避免长文本时出错 1 年前 |
|
|
Megrez7 · C#ToArray转换合并为一行,导致数组元素更改 1 年前 |
|
Aycon · 在工厂方法中释放部分创建的对象的正确方法是什么? 1 年前 |
|
|
Sei · Avalonia/WPF将路由器传递到控制模板 1 年前 |