gpt4 book ai didi

multithreading - 为什么线程如此昂贵,事件驱动的非阻塞 IO 在基准测试中更好

转载 作者:行者123 更新时间:2023-12-04 11:10:09 25 4
gpt4 key购买 nike

我最近开始学习 node.js,这是一个基于 V8 的 JavaScript 库,以其非阻塞 IO 和令人难以置信的速度而闻名。

据我了解,节点不会等待 IO 响应,而是运行一个事件循环(类似于游戏循环),不断检查未完成的操作并在 IO 响应后立即继续/完成它们。节点性能与 Apache HTTPD 进行了比较,节点在使用更少内存的情况下明显更快。

现在,如果您阅读有关 Apache 的信息,您会了解到它每个用户使用 1 个线程,这可能会显着减慢它的速度,这就是我的问题出现的地方:

如果将线程与节点在其事件循环内部执行的操作进行比较,您就会开始看到相似之处:两者都是等待资源响应的未完成进程的抽象,两者都检查操作是否有规律地取得进展,然后不占用CPU 一段时间(至少我认为一个好的阻塞 API 在重新检查之前会休眠几毫秒)。

现在,使线程变得如此糟糕的惊人的关键差异在哪里?

最佳答案

这里的区别是上下文切换。操作系统交换线程需要:

  • 保存指令指针(由 CPU 完成)
  • 保存 CPU 寄存器(如果线程进行了阻塞调用,则可能不需要,但如果被抢占则需要)
  • 交换调用堆栈。即使堆栈驻留在相同的虚拟内存空间中,这至少是一次写入和一些读取,甚至适用于微线程(纤程)。
  • 在切换到不同进程的情况下,切换到内核模式,更新虚拟内存表并返回到用户模式。

  • 在事件队列的情况下:
  • 状态已更新。这在任何情况下都需要发生。
  • 事件处理程序返回。不是交换调用堆栈,而是弹出当前调用堆栈。
  • 检查事件队列是否有待处理的请求。只有当没有挂起的请求时,应用程序才会等待。这可以通过重复 sleep (如 OP 所建议的)或(更好)通过对事件队列进行阻塞调用来完成。如果事件队列(例如一组 TCP 套接字)由操作系统管理,则操作系统负责将新事件通知应用程序(套接字可以接受更多数据)。

  • 如果服务器负载很高,事件队列的唯一开销是处理程序返回、读取队列和处理程序调用。在线程方法中,交换线程会产生额外的开销。

    另外,正如 PST 所提到的,线程方法引入了锁定的需要。锁定本身很便宜,但等待某个其他线程释放资源需要额外的上下文切换,因为等待线程无法继续。甚至有可能一个线程被换入以获得一个锁,但在几个时钟周期后才被换出,因为它也需要锁定另一个资源。将操作系统完成的工作量(至少读取线程队列和交换调用堆栈)与线程完成的工作量(从调用返回并进行另一个调用)进行比较。

    关于multithreading - 为什么线程如此昂贵,事件驱动的非阻塞 IO 在基准测试中更好,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13106075/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com