我最近开始学习node.js,这是一个以V8为基础的javascript库,以其非阻塞IO和令人难以置信的速度而闻名。
据我所知,节点不会等待IO响应,而是运行一个事件循环(类似于游戏循环),它会一直检查未完成的操作,并在IO响应时继续/完成它们。将节点性能与Apache HTTPD进行比较,节点使用较少的内存时速度明显加快。
现在,如果你阅读了关于Apache的内容,你会发现它每个用户使用1个线程,这可能会大大降低它的速度,这就是我的问题所在:
如果将线程与事件循环中内部节点进行比较,则会开始看到相似之处:两者都是等待资源响应的未完成进程的抽象,都检查操作是否定期进行,然后再进行操作t占用CPU一段时间(至少我认为一个好的阻塞API在重新检查之前会休眠几毫秒)。
现在哪个引人注目的关键差异让线程变得更糟?
答案 0 :(得分:10)
这里的区别在于上下文切换。操作系统交换线程需要:
如果是事件队列:
如果服务器高度加载,则事件队列的唯一开销是处理程序返回,读取队列和处理程序调用。在线程方法中,交换线程会产生额外的开销。
另外,正如PST所提到的,线程方法引入了锁定的需要。锁定本身很便宜,但是等待某个其他线程释放资源需要额外的上下文切换,因为等待线程无法继续。甚至可以交换一个线程来获取一个锁,只是为了在几个时钟周期之后被换出,因为它还需要锁定另一个资源。比较操作系统(读取胎面队列和交换调用堆栈,至少)完成了多少工作(通过调用返回并进行另一次调用)。
答案 1 :(得分:0)
从一个方面来看,它确实取决于特定于该语言的线程的实现。但是,一般来说,创建一个线程是一个代价高昂的部分,而不是线程的运行。因此,某些语言(如.Net)保留了一个线程池线程,因此您可以获取一个基本上已经创建的线程,从而降低成本。
从教授告诉我的话,线程的问题也在于每个语言都具有Thread.Yield()函数的等价物,但实际上并没有人使用它;因此,您将遇到的每个线程在调度方面都非常积极,这会在互斥锁和sempaphores之间建立各种各样的战争;一些线程,由于使用的攻击程度,从未实际运行,这本身就是一个问题。
线程的好处是它们以增加功能为代价从其他循环中卸载功能,例如GUI循环。据我所知,事件仍然在一个线程中运行(除非特别说明不这样做)。