是非阻塞I / O真快于多线程阻塞I / O?怎么样?

问题描述 投票:104回答:9

我搜索关于阻塞I / O和非阻塞I / O一些技术细节在网上,我发现有几个人指出,非阻塞I / O会比阻塞I / O速度更快。例如,在this document

如果我使用阻塞I / O,那么当然是目前受阻不能做别的事...因为它阻塞线程。但是,一旦一个线程开始被阻塞后,操作系统可以切换到另一个线程,而不是切换回,直到有东西阻塞的线程做。因此,只要有需要CPU,而不是阻塞系统上的其它线程,不应该有比事件更多的CPU空闲时间基于无阻塞的做法,是吗?

除了减少时间的CPU空闲时我看到一个更多的选择,增加一台计算机可以在给定的时间内完成的任务数:减少切换线程引入的开销。但如何才能做到这一点?而且是开销大到足以显示可测量的效果?下面是我怎么能想象它的工作的想法:

  1. 要加载文件的内容,应用代表这个任务到基于事件的I / O框架,传递一个回调函数与文件名一起
  2. 该事件框架委托给操作系统,硬盘的DMA控制器可以直接将文件写入其中的程序内存
  3. 事件框架允许进一步的代码运行。
  4. 一旦磁盘到存储器复制完成时,DMA控制器使得一个中断。
  5. 操作系统的中断处理程序通知有关文件基于事件的I / O框架被完全加载到内存中。它是如何做到这一点?使用信号?
  6. 当前事件我中运行的代码/ O框架完成。
  7. 基于事件的I / O框架检查其队列并从步骤5看到操作系统的信息,并执行在步骤1得到的回调。

那是它是如何工作的?如果不是这样,它是如何工作的?这意味着,事件系统可以而不必需要明确地接触到纸堆的工作(如将需要备份的堆栈和另一个线程的堆栈复制到内存,而切换线程真正的调度)?多少时间,这实际上节省?是否有更多的东西?

multithreading io blocking nonblocking
9个回答
43
投票

非阻塞或异步I的最大优点/ O是你的线程可以继续它在并行工作。当然你也可以做到这一点还使用附加的线程。至于你所说的最佳的整体(系统)的性能我想这将是更好地使用异步I / O,而不是多线程(因此减少线程切换)。

让我们看一个网络服务器程序,依法办理1000个客户端并联连接的可能实现:

  1. 每个连接一个线程(可阻塞I / O,但也可以是非阻塞I / O)。 每个线程都需要内存资源(也内核内存!),这是一个缺点。而每增加线程用于调度更多的工作。
  2. 一个线程用于所有连接。 这需要负载从系统,因为我们有较少的线程。但它也可以防止您使用本机的全部性能,因为你可能最终驱动一个处理器为100%,让所有其他处理器空闲左右。
  3. 几个线程,每个线程处理一些连接。 这需要从负荷系统,因为有较少的线程。它可以使用所有可用的处理器。在Windows中,这个方法是由Thread Pool API支持。

当然,有更多的线程本身并不是一个问题。正如你可能已经认识到,我选择了一个相当高的连接数/线程。我怀疑你会看到三种可能的实现之间的区别,如果我们谈论的只有十几个线程(这也是什么雷蒙德陈建议在MSDN博客文章Does Windows have a limit of 2000 threads per process?)。

在Windows上使用unbuffered file I/O手段写入必须是一个尺寸是页面大小的倍数。我没有测试它,但它听起来像,这也可能影响积极为缓冲的同步和异步写入的写入性能。

1至7的步骤你描述给它是如何工作是个好主意。在Windows操作系统将使用事件或回调通知你的异步I / O(WriteFileOVERLAPPED结构)的完成。回调函数才会被调用,例如当你的代码调用WaitForMultipleObjectsExbAlertable设置为true

在网络上的一些更多阅读:


28
投票

I / O包括多个有点像从硬盘读取和写入数据,访问网络资源,调用Web服务或从数据库中检索数据的操作。根据不同的平台和操作的类型,异步I / O通常会利用任何硬件或低级别的系统支持执行操作。这意味着它将与尽可能小的影响CPU上执行。

在应用层面上,异步I / O防止线程不必等待I / O操作完成。作为作为异步I / O操作一旦启动,它会释放其所推出的线程和回调注册。操作完成后,回调是排队在第一个可用的线程执行。

如果同步执行I / O操作,它保持其运行的线程什么也不做,直到操作完成。运行时不知道的I / O操作完成时,所以它会定期提供一些CPU时间来等待线程,可能另有由具有实际的CPU绑定操作来执行其他线程使用的CPU时间。

因此,作为@ user1629468提到的,异步I / O并没有提供更好的性能,而是更好的可伸缩性。在具有线程的数量有限,喜欢它是与Web应用程序的情况下,上下文中运行的时,这是显而易见的。 Web应用程序通常使用从他们的线程分配给每个请求的线程池。如果请求被阻塞在长时间运行的I / O操作有消耗网络池,使Web应用程序冻结或响应缓慢的风险。

有一两件事我注意到的是,具有非常快的I / O操作打交道时,异步I / O是不是最好的选择。在这种情况下,不保持一个线程忙于在等待I / O操作完成不是很重要,操作上启动一个线程,它是完成了另一事实的利益增加了开销的整体执行。

你可以阅读更详细的研究,我最近异步I的话题/ O与多线程here制造。


4
投票

使用AIO的主​​要原因是可扩展性。当几个线程的上下文看,好处并不明显。但是,当系统扩展到螺纹1000,AIO将提供更好的性能。需要说明的是,AIO图书馆不应该引入进一步的瓶颈。


4
投票

要设定一个速度的提高所导致的任何形式的多计算,你必须假定,要么多基于CPU的任务正在于多个计算资源(通常是处理器核心)并行执行,否则,不是所有的任务,依靠的并发使用相同的资源 - 即,某些任务可取决于一个系统子组件(磁盘存储,说),而一些任务取决于另一个(接收通信从外围设备),并且仍然其他人可能需要处理器核的用法。

第一种情形是经常被称为“平行”的编程。第二种情况是经常被称为有时也用于指的仅仅允许操作系统来交错的多个任务,执行的情况下,不管这样的执行是否必须采取“同时”或“异步”编程,虽然“并发”串联放置或如果多个资源可用于实现并行执行。在该后一种情况下,“同时”通常指的是执行写在程序中的方式,而不是从任务执行的实际同时性的透视图。

这很容易谈论这一切与默契的假设。例如,一些是快速做出如权利要求诸如“异步的I / O会比多线程I / O更快”。这种说法是可疑的有以下几个原因。首先,它可能是某个给定的异步I / O架构与多线程正是实现的情况下,在这种情况下,他们在同一个,它没有任何意义说一个概念“快于”其他。

其次,即使是在案件时,有一个单线程执行异步框架(如单线程事件循环),你仍然必须对什么是循环做一个假设。例如,您可以用单线程事件循环做一个愚蠢的事情是要求它异步完成两种不同的纯计算机密集型任务。如果你只有一个理想化的单处理器核心(忽略现代化的硬件优化),然后执行此任务做此一台机器上“异步”不会真的比使用两个独立的托管线程,或者只是一个孤独的过程完成执行任何不同 - - 区别可能回落到线程上下文切换或操作系统调度优化,但如果两个任务都将CPU时,它会在两种情况下类似。

它想象很多你可能会遇到不寻常的或愚蠢的角落的情况下是非常有用的。

“异步”并不一定是并发的,例如以上只是:你“异步”的机器上执行两个CPU密集型任务可以精确到一个处理器核心。

多线程执行不具有并发:你生成一个机器上的两个线程与单个处理器核心,或要求两个线程获得任何其他种类的稀缺资源的(想象一下,比方说,一个网络数据库,只能建立一个同时连接)。该线程的执行可能然而交织的操作系统调度程序认为合适的,但不能减少他们的总运行时间(并从线程上下文切换增加)在单核(或者更一般地,如果你产生更多的线程比有内核来运行它们,或有更多的线程,要求比什么资源可以承受的资源)。相同的情况也适用于多处理为好。

因此,不管是异步I / O,也没有多线程必须提供在运行时间方面的任何性能增益。他们甚至可以慢下来。

如果你定义一个特定的使用情况,但是,就像一个特定的程序,既让网络通话从像远程数据库与网络连接的资源获取数据,并且也做了一些当地的CPU密集型的计算,那么你就可以开始推理这两种方法之间的性能差异给有关硬件的特定假设。

该问题要问:多少计算步骤,我需要进行多少独立的系统资源都在那里进行呢?是否有那些需要的独立的系统使用子,并且可以从这样做同时受益的计算步骤的子集?多少个处理器内核我有,什么是用于使用多个处理器或线程来完成独立内核任务的开销?

如果你的任务很大程度上依赖于独立的子系统,然后异步的解决方案可能是很好的。如果处理这将是大需要的线程数量,这样的上下文切换成为不平凡的操作系统,然后单线程异步解决方案可能会更好。

每当任务由相同的资源约束(如多发性需要同时访问同一个网络或本地资源),那么多线程可能会引进不满意开销,而单线程异步可能引入更少的开销,在这样的资源 - 有限的情况下它也不能产生加速。在这种情况下,唯一的选择(如果你想有一个加速)是提供该资源的多个副本(例如,多个处理器核心,如果稀缺资源是CPU;支持更多的并发连接一个更好的数据库,如果稀缺资源是一个连接限制数据库,等等)。

把它的另一种方法是:允许操作系统交错的两个任务单个资源的使用不能不是仅仅让一个任务中使用的资源,而其他等待,然后让第二个任务结束连续快。此外,交错的调度成本是指在任何真实的情况,它实际上创建了一个放缓。如果CPU,网络资源,存储器资源,外围设备,或任何其他系统资源的发生交错的使用也没关系。


2
投票

一种可能的实现非阻塞I / O的是你说什么,用的那些阻塞I / O,并通过一些回调机制通知I / O的鼻祖线程后台线程池。事实上,这是glibc中的AIO模块是如何工作的。 Here即将实施的几个模糊的细节。

虽然这是一个很好的解决方案,是相当便携(只要你有线程),操作系统通常是能够服务更有效地非阻塞I / O。 This Wikipedia article列出除了线程池可能的实现。


2
投票

我目前在使用protothreads嵌入式平台上实现异步IO的过程。非阻塞IO使得在16000fps和160fps运行之间的差异。非阻塞IO的最大好处是,你可以组织你的代码做其他的事情,而硬件做它的事。即使设备的初始化可以并行进行。

马丁


1
投票

在节点,多个线程正在启​​动,但它是在C ++运行时的一层下来。

“所以,是的NodeJS是单线程的,但是这是一个半真半假的话,实际上它是事件驱动和单线程与背景的工人,主要事件循环是单线程的,但大多数的I / O工作在单独的线程运行,因为I /在Node.js的öAPI是由设计异步/无阻塞,以便适应事件循环“。

https://codeburst.io/how-node-js-single-thread-mechanism-work-understanding-event-loop-in-nodejs-230f7440b0ea

“Node.js的是非阻塞的,这意味着所有的功能(回调)委派给事件循环和它们是(或可以)通过不同线程上执行。这是通过Node.js的运行时间进行处理。”

https://itnext.io/multi-threading-and-multi-process-in-node-js-ffa5bb5cde98

“节是更快,因为它是不可阻挡......”的解释是有点营销,这是一个很大的问题。它的高效,可扩展的,但不完全是单线程的。


0
投票

改善据我知道的是,异步I / O的使用(我说的是MS系统,只是为了澄清)所谓called I/O completion ports。通过使用异步自动调用框架杠杆这样的架构,而这被认为是非常有效的标准线程机制。作为个人的经验,我可以说,你会觉得明智的您的应用程序更多的反应,如果你喜欢AsyncCalls而不是阻塞的线程。


0
投票

让我给你一个反异步I / O不起作用。我写类似下面,使用boost :: ASIO的代理。 https://github.com/ArashPartow/proxy/blob/master/tcpproxy_server.cpp

然而,我的情况的情况是,进入的(从客户端)消息快,而流出(服务器端)是缓慢的一个会议上,跟上进入的速度或最大化总代理吞吐,我们必须使用下一个连接多个会话。

因此,这种异步I / O架构不工作了。我们确实需要一个线程池,通过分配每个线程的会话发送到服务器。

© www.soinside.com 2019 - 2024. All rights reserved.