当当前线程阻塞时,OS可以主动切换到另一个线程吗?如果是这样,它是否使异步编程的价值大大降低?



好的,让我试着重复一下这两个问题:

  1. OS是否在线程开始阻塞时主动抢占线程,并且在阻塞完成之前永远不会返回线程?我觉得操作系统有关于磁盘IO和网络IO的信息,所以它应该有足够的信息来做这些。
  2. 如果操作系统可以通过切换到另一个线程来消除CPU空闲时间,我们真的需要异步编程吗?

因此,即使线程阻塞,CPU也没有阻塞但是正在运行其他线程

对,是这样。

如果我的理解是正确的,值是多少异步编程吗?

如果你想让你的程序同时在多个任务上取得进展,这是很有用的。

当然,你可以通过显式地生成多个线程来获得同样的效果,并让每个线程在一个单独的任务上工作(包括任何阻塞调用),这也可以工作,但是你必须显式地管理所有这些线程,这可能有点痛苦。(线程间同步/通信可能很棘手,特别是当你想要取消一个操作时,如果你的一个或多个线程被阻塞在一个阻塞的I/O调用中,因此不能轻易地说服它快速退出,那么你的其他线程可能必须等待很长时间,可能永远,才能join()那个线程并安全终止)

到目前为止,异步编程的复兴与操作系统/内核体系结构关系不大。自20世纪60年代以来,操作系统就阻碍了你所描述的方式;尽管原因有所改变。

在早期的系统中,效率是通过有用的CPU周期来衡量的;因此,当一个任务受阻时切换任务是一种自然的行为。

现代系统架构经常解决如何保持cpu占用;例如,如果有800个cpu,但有20个任务;780个cpu无事可做

作为一个具体的例子,对一个文件的所有字节求和的程序可能看起来有点像:

for (count=0;  (c = getchar()) != EOF; count += c) {}

一个多线程版本,为了提高性能,可能看起来像:

for (n=0; n < NCPU; n++) {
if (threadfork() == 0) {
offset_t o = n* (file_size / NCPU);
offset_t l = (file_size / NCPU);
for (count = 0; l-- && pread(fd, &c, 1, o) == 1; count += c) {}
threadexit(count);
}
}
for (n=0; n < NCPU; n++) {
threadwait(&temp);
total += temp;
}
return total;

有点可怕,因为它很复杂,而且可能有不一致的加速。对比函数:

int GetSum(char *data, int len) {
int count = 0;
while (len--) {
count += *data++;
}
return count;
}

我可以构造一种调度器,当ram中有一块文件数据可用时,调用GetSum(),将其返回值排队以便稍后累积。这个调度器可以在熟悉最佳i/o模式等方面进行投资。因为它可以适用于许多问题;和程序员有一个相当简单的工作要做。

即使没有那种原生支持;我可以mmap(2)文件,然后调度多个线程来触摸一个页面,然后在该页面上调用GetSum。这将有效地模拟普通旧unix-y框架中的异步模型。

当然没有那么容易;在面向调度的异步模型中,甚至进度条都是可疑的(并不是说20世纪50年代基于顺序的进度条有什么值得大篇大论的)。沟通错误也很麻烦;因为你使用异步来将最大的CPU资源分配给你自己,所以你需要最小化同步操作。

Async有很多可能性;但它确实需要具有事实上的异步支持的语言,而不是从最新的标准中获得一些摇摇欲坠的语言。