异步(非阻塞)代码的可伸缩性优势是什么?

Win*_*ith 10 c# multithreading asynchronous scalability

由于以下两个主要原因,阻塞线程被认为是不好的做法:

  1. 线程花费内存.
  2. 线程通过上下文切换花费处理时间.

以下是我遇到这些原因的困难:

  1. 非阻塞,异步代码也应该花费几乎相同的内存量,因为调用堆栈应该正确执行他异步调用(背景前的地方保存保存,毕竟).如果线程效率非常低(内存方面),为什么OS/CLR不提供更轻量级的线程(只保存callstack的上下文而不是其他内容)?它不是一个更清洁的内存问题解决方案,而不是强迫我们以异步方式重新构建我们的程序(这更复杂,更难理解和维护)?

  2. 当线程被阻塞时,操作系统将其置于等待状态.操作系统不会上下文切换到休眠线程.由于超过95%的线程生命周期花费在休眠上(假设这里有IO绑定的应用程序),性能命中应该可以忽略不计,因为线程的处理部分可能不会被操作系统抢占,因为它们应该跑得很快,做得很少.因此,在性能方面,我无法看到非阻塞方法带来很多好处.

我在这里错过了什么或为什么这些论点有缺陷?

Eri*_*ert 16

非阻塞,异步代码也应该花费相同数量的内存,因为在执行异步调用之前应该将callstack保存在某处(毕竟保存了上下文).

发生时保存整个调用堆栈await.为什么你认为需要保存整个调用堆栈? 调用堆栈是延续的具体化和延续了期待已久的任务不是延续的AWAIT.await的延续在堆栈上.

现在,情况很可能是当等待给定调用堆栈中的每个异步方法时,等效于调用堆栈的信息已存储在每个任务的延续中.但是这些延续的内存负担是垃圾收集堆内存,而不是一百万字节的已提交堆栈内存.连续状态大小是任务数量的大小n; 无论你是否使用它,线程的负担都是一百万字节.

如果线程效率非常低(以内存方式),为什么OS/CLR不提供更轻量级的线程

操作系统.它提供纤维.当然,纤维仍然有堆叠,所以这可能不会更好.我想你可以有一个小堆栈的线程.

它不是一个更清洁的内存问题解决方案,而不是强迫我们以异步方式重新构建我们的程序

假设我们制作线程 - 或者就此而言,流程 - 便宜得多.这仍然无法解决同步访问共享内存的问题.

对于它的价值,我认为如果工艺重量更轻,那将是很好的.他们不是.

而且,这个问题有点自相矛盾.您正在使用线程,因此您已经愿意承担管理异步操作的负担.给定的线程必须能够在产生第一个线程要求的结果时告诉另一个线程.线程已经暗示异步,但异步并不意味着线程化.拥有内置于语言的异步体系结构,运行时和类型系统只会使不幸的人必须编写管理线程的代码.

由于超过95%的线程生命周期花费在休眠上(假设这里有IO绑定的应用程序),性能命中应该可以忽略不计,因为线程的处理部分可能不会被操作系统抢占,因为它们应该跑得很快,做得很少.

为什么你会雇用一个工人(线程)并支付他们的工资来坐在等待邮件到达的邮箱(睡觉线程)(处理IO消息)?IO中断首先不需要线程.IO中断存在于线程级别以下的世界中.

不要雇佣一个线程来等待IO; 让操作系统处理异步IO操作.雇佣线程进行疯狂的大量高延迟CPU处理,然后为您拥有的每个CPU分配一个线程.

现在我们来回答你的问题:

异步(非阻塞)代码有什么好处?

  • 不阻止UI线程
  • 可以更轻松地编写生活在高延迟世界中的程序
  • 更有效地利用有限的CPU资源

但是,让我用一个类比来重新解释这个问题.你在经营一家快递公司.有许多订单进入,许多交货已经结束,您无法告诉客户您在他们完成之前的每次交货之前都不会收货.哪个更好:

  • 雇用五十个人接听电话,领取包裹,安排送货和送货包裹,然后要求其中46人随时闲置

  • 雇用四个人,让他们每个人一开始都做得很好,一次做一点工作,这样他们总是能够响应客户的要求,其次,他们非常擅长保留他们需要的工作清单.做到了

后者对我来说似乎更好.

  • 并且CLR中没有内置的光纤支持.至于你的最后一点,如果他这么便宜,我为什么要关心那个工人坐在邮箱旁边呢?当他睡觉时,他不花钱(CPU),当他醒来时,他的工作速度很快,以至于上下文切换问题应该是无问题的.当然,存在内存问题,但老实说,无论如何,这都不是什么大问题. (3认同)