异步IO、APC、IO完成端口、线程池与高性能服务器之一 异步IO
- 格式:doc
- 大小:116.50 KB
- 文档页数:28
linux异步io实现方式Linux异步IO(Asynchronous I/O)是一种实现I/O操作的方式,它与传统的同步IO(Synchronous I/O)相比具有更高的效率和更好的性能。
本文将介绍Linux异步IO的实现方式。
Linux异步IO的实现方式主要有以下几种:多线程方式、信号方式、回调函数方式和事件驱动方式。
1. 多线程方式:在多线程方式中,主线程负责发起IO请求,然后创建一个或多个工作线程来处理这些请求。
主线程启动一个线程池,每个线程负责一个IO操作。
主线程将IO请求分配给空闲的工作线程,工作线程独立地进行IO操作。
这种方式的优点是简单易用,但需要管理线程池和线程间的同步和通信。
2. 信号方式:在信号方式中,主线程发起IO请求后,将信号设置为非阻塞模式,然后继续执行其他任务。
当IO操作完成时,内核会发送一个信号通知主线程。
主线程通过信号处理函数来处理完成的IO操作。
这种方式的优点是简单高效,但需要处理信号的并发性和可靠性。
3. 回调函数方式:在回调函数方式中,主线程发起IO请求后,将回调函数注册到内核中,并继续执行其他任务。
当IO操作完成时,内核会调用注册的回调函数来处理完成的IO操作。
这种方式的优点是灵活性高,但需要管理回调函数的注册和执行。
4. 事件驱动方式:在事件驱动方式中,主线程发起IO请求后,将IO事件添加到事件循环中,并继续执行其他任务。
事件循环会监听所有IO事件,并根据事件类型调用相应的处理函数。
这种方式的优点是高效灵活,但需要管理事件循环和事件处理函数。
总结起来,Linux异步IO的实现方式有多线程方式、信号方式、回调函数方式和事件驱动方式。
不同的方式适用于不同的场景,开发者可以根据实际需求选择合适的实现方式。
异步IO可以提高系统的并发性和性能,使系统能够更好地处理大量的IO操作。
tornado 示例
Tornado是一种基于Python的Web框架,它的灵活性和高性能被广泛地应用于Web开发领域。
以下是一些Tornado示例:
1. Web聊天室:使用Tornado的WebSocket来实现实时聊天功能,这是一种非常流行的Web应用场景。
用户可以实时地发送和接收消息,而且可以支持多个用户同时聊天。
2. RESTful API:在Tornado中编写RESTful API非常简单,只需要定义不同的HTTP方法和相应的处理函数即可。
RESTful API可以用于实现各种Web应用,例如博客、社交网络、电子商务等等。
3. 异步IO:Tornado是异步IO的典范之一,它的协程机制可以使得Python的代码像同步代码一样易于编写,同时又能获得异步IO 的高性能。
在Tornado中使用异步IO可以提高Web应用的吞吐量,从而更好地应对高并发的情况。
4. 定时任务:Tornado中的定时任务可以使用
tornado.ioloop.PeriodicCallback来实现,这个函数可以在指定的时间间隔内循环执行某个函数。
这种机制非常适合实现一些高效的后台任务,例如数据清洗、日志记录等等。
5. 数据库连接:Tornado可以与多种数据库进行连接,例如MySQL、PostgreSQL、MongoDB、Redis等等。
这样可以方便地将Web 应用与持久化存储结合起来,实现数据的存储和查询。
- 1 -。
异步IO、APC、IO完成端口、线程池与高性能服务器背景:轮询PIO DMA 中断早期IO设备的速度与CPU相比,还不是太悬殊。
CPU定时轮询一遍IO设备,看看有无处理要求,有则加以处理,完成后返回继续工作。
至今,软盘驱动器还保留着这种轮询工作方式。
随着CPU性能的迅速提高,这种效率低下的工作方式浪费了大量的CPU时间。
因此,中断工作方式开始成为普遍采用的技术。
这种技术使得IO设备在需要得到服务时,能够产生一个硬件中断,迫使CPU放弃目前的处理任务,进入特定的中断服务过程,中断服务完成后,再继续原先的处理。
这样一来,IO设备和CPU可以同时进行处理,从而避免了CPU等待IO完成。
早期数据的传输方式主要是PIO(程控IO)方式。
通过对IO地址编程方式的方式来传输数据。
比如串行口,软件每次往串行口上写一个字节数据,串口设备完成传输任务后,将会产生一个中断,然后软件再次重复直到全部数据发送完成。
性能更好的硬件设备提供一个FIFO(先进先出缓冲部件),可以让软件一次传输更多的字节。
显然,使用PIO方式对于高速IO设备来说,还是占用了太多的CPU时间(因为需要通过CPU编程控制传输)。
而DMA(直接内存访问)方式能够极大地减少CPU处理时间。
CPU仅需告诉DMA控制器数据块的起始地址和大小,以后DMA控制器就可以自行在内存和设备之间传输数据,其间CPU可以处理其他任务。
数据传输完毕后将会产生一个中断。
同步文件IO和异步文件IO下面摘抄于MSDN《synchronous file I/O and asynchronous file I/O》。
有两种类型的文件IO同步:同步文件IO和异步文件IO。
异步文件IO也就是重叠IO。
在同步文件IO中,线程启动一个IO操作然后就立即进入等待状态,直到IO操作完成后才醒来继续执行。
而异步文件IO方式中,线程发送一个IO请求到内核,然后继续处理其他的事情,内核完成IO请求后,将会通知线程IO 操作完成了。
Windows异步I/O和完成端口上周做了一次关于Windows异步I/O和完成端口的部门技术分享,着重于理论介绍,顺带review 基于IOCP的网络库代码。
完成端口是异步I/O的一种,将这两个并列作为标题,因为完成端口的复杂性及用途相比其他几种异步I/O加起来还过之。
Windows核心编程中关于设备异步I/O介绍的很明白,这里再回顾一下。
先区分两组概念:同步/异步(Async/Sync),阻塞/非阻塞(blocking/non-blocking)这两组概念很暧昧,有时可以不用分的很清楚。
我将这两组概念分别定性为面向结果和面向过程。
想象生活中排队的场景。
印象中排队最痛苦的是2010那年在上海观看世博会,每进一个场馆要排上2-5个小时,而且天气燥热,排队过程中异常难耐。
另外一种进场馆的方式就是凭预约票,可以在当天按票上时间段进场。
排队进馆就是一种同步方式,直到队伍中轮到你才可以进去,凭票进馆则是异步方式,两种方式都是面向同一个结果:进馆。
但是排队过程中,只能在队伍中等待,意味着行为失去自由,也就是阻塞,什么事也做不了,而凭票则可以在进馆之前到任何其他想去的地方,行动自由,也就是非阻塞的。
从这个例子中可以看出为什么喜欢异步和非阻塞了,体现在程序中,则是它们的性能差别(可以看这篇)。
现在开始介绍异步I/O。
一、请求异步I/O的条件任何异步I/O都需要有系统设施的支撑,正如取票进馆需要有预约票作凭证。
请求异步I/O也需要两个基本条件:队列和重叠结构(OVERLAPPED,重叠是指I/O请求的时间和线程执行其他任务的时间是重叠的)。
1typedef struct _OVERLAPPED {2ULONG_PTR Internal; // I/O请求错误码3ULONG_PTR InternalHigh; // 已传输字节数4DWORD Offset; // I/O操作位置(低、高位),非文件设备忽略5DWORD OffsetHigh;6HANDLE hEvent; // 内核事件对象或自定义结构7}OVERLAPPED;队列由设备驱动程序维护,记录I/O请求信息,其中包含数据地址和重叠结构的信息。
一文读懂网络编程中的5种IO模型关于IO模型,就必须先谈到几个日常接触的几个与IO相关名字:同步,异步,阻塞,非阻塞。
一、名词解释1、同步如果事件A需要等待事件B的完成才能完成,这种串行执行机制可以说是同步的,这是一种可靠的任务序列,要么都成功,要么都失败。
2、异步如果事件A的执行不需要依赖事件B的完成结果,这种并行的执行机制可以说是异步的。
事件A不确定事件B是否真正完成,所以是不可靠的任务序列。
同步异步可以理解为多个事件的执行方式和执行时机如何,是串行等待还是并行执行。
同步中依赖事件等待被依赖事件的完成,然后触发自身开始执行,异步中依赖事件不需要等待被依赖事件,可以和被依赖事件并行执行,被依赖事件执行完成后,可以通过回调、通知等方式告知依赖事件。
3、阻塞对于阻塞,如果一个事件在发起一个调用之后,在调用结果返回之前,该事件会被一直挂起,处于等待状态。
4、非阻塞对于非阻塞,如果一个事件在发起调用以后,无论该调用当前是否得到结果,都会立刻返回,不会阻塞当前事件。
阻塞与非阻塞可以理解为单个事件在发起其他调用以后,自身的状态如何,是苦苦等待还是继续干自己的事情。
非阻塞虽然能提高CPU利用率,但是也带来了系统线程切换的成本,需要在CPU执行时间和系统切换成本之间好好估量一下。
二、IO模型IO模型分为五种,阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动IO模型、异步IO模型、前4种为同步IO操作、只有异步IO模型是异步IO操作、请仔细阅读IO交互便于理解IO模型。
1、阻塞IO模型网络编程中,读取客户端的数据需要调用recvfrom。
在默认情况下,这个调用会一直阻塞直到数据接收完毕,就是一个同步阻塞的IO方式。
模拟举例:老李去火车站买票,排队三天买到一张退票。
耗费:在车站吃喝拉撒睡 3天,其他事一件没干。
2、非阻塞IO模型当用户进程发出read操作时、如果内核中的数据还没有准备好、那么它并不会阻塞用户进程、而是立刻返回一个error、从用户进程角度讲、它发起一个read操作后、并不需要等待、而是马上就得到了一个结果、用户进程判断结果是一个error时、它就知道数据还没有准备好、于是它可以再次发送read操作、一旦内核中的数据准备好了、并且又再次收到了用户进程的系统调用、那么它马上就将数据拷贝到了用户内存、然后返回。
同步io和异步io
I/O(输入/输出)是指计算机与外部设备之间的数据交换,可将外部设备当作内存的一部分去使用,其中包括存储器、输入输出设备等。
I/O有两种不同的模式:同步IO和异步IO。
同步IO意味着,当一个进程要进行I/O操作时,它会进入等待状态,直到I/O操作完成,然后,继续执行。
这种方式显得有点低效,因为它会消耗比较多的CPU资源,以及在等待的时候,可能会影响到系统的其他部分的执行效率。
而异步IO则不同,它不会让进程进入等待状态,而是在I/O操作完成时,内核自动将它的状态变成“完成”,然后,进程就可以马上重新被调度,继续执行。
但是,异步IO本身没有提供任何确保I/O 操作成功完成的机制,进程在发出I/O请求后,并不能立刻就知道I/O操作是否成功完成,只能等待它的状态变成“完成”通知。
异步IO会比同步IO更加高效,因为当进程发出IO请求时,可以立即重新调度其他进程,而不需要等待I/O操作完成,从而提高了系统的整体效率。
但是,在某些特殊的情况下,并不能使用异步IO,比如有关紧密的数据传递,或者需要在发出I/O请求后立刻就知道I/O操作最终是否成功完成。
这时,一般就可以使用同步IO,因为它能确保I/O操作最终完成。
总之,同步IO和异步IO都有其各自的优势和缺点,根据实际情况及需求,应该合理选择适当的IO模式,才能提高I/O操作的效率。
- 1 -。
python 协程使用场景Python协程是一种并发编程的方式,它可以在单线程中实现多个任务的并发执行。
Python协程的使用场景非常广泛,可以用于各种需要并发处理的应用场景。
本文将介绍Python协程的几个常见使用场景。
1. 异步IO在网络通信、数据库访问、文件读写等IO密集型的应用中,使用Python协程可以提高程序的并发处理能力。
通过使用异步IO库,可以在进行IO操作的同时,执行其他任务,充分利用CPU资源。
2. 高并发服务器Python协程可以用于构建高并发的服务器,提供并发的网络服务。
通过使用协程,可以在一个线程中处理多个客户端请求,避免了线程切换的开销,提高了服务器的并发处理能力。
3. 爬虫在爬取大量的网页数据时,使用Python协程可以提高爬虫的效率。
通过使用异步HTTP库,可以并发发送多个HTTP请求,提高数据的获取速度。
同时,可以通过使用协程来处理解析网页和存储数据等任务,提高整个爬虫系统的并发处理能力。
4. 并行计算Python协程可以用于并行计算任务,提高计算的效率。
通过将计算任务分解为多个小任务,并使用协程来执行这些小任务,可以充分利用CPU资源,提高计算的并发处理能力。
5. 实时数据处理在实时数据处理的场景下,Python协程可以用于处理数据流。
通过使用协程来处理数据的输入、处理和输出等任务,可以实现实时的数据处理和分析,提高系统的实时性。
6. 事件驱动编程Python协程可以用于事件驱动的编程模型。
通过使用协程来处理事件的监听和处理等任务,可以实现事件驱动的编程模型,提高系统的响应速度和并发处理能力。
7. 状态机Python协程可以用于实现状态机。
通过使用协程来表示不同的状态,并在不同的状态之间切换,可以实现复杂的状态机逻辑。
这在游戏开发、自动化控制等领域非常有用。
总结:Python协程的使用场景非常广泛,可以用于异步IO、高并发服务器、爬虫、并行计算、实时数据处理、事件驱动编程和状态机等应用场景。
五种io模型区别通俗理解哎呀,说起这五种IO模型的区别啊,咱就通俗点儿讲,像四川人摆龙门阵一样,娓娓道来哈。
首先,咱得明白啥子是IO模型。
IO模型,就是输入输出模型,就像咱们吃饭,嘴巴是输入,肚子是处理,拉出来就是输出,一个道理。
那五种IO 模型呢,就是五种不同的吃法,各有各的特点。
咱们先从同步IO开始摆。
这就像你在馆子里头点菜,点完了就等着,菜好了服务员给你端上来,你吃完再走。
这就是同步IO,你得等着IO操作完成才能继续干别的事儿。
然后咱说说异步IO。
这个就像你点了个外卖,点完就可以干别的事儿去了,不用等着。
等外卖送到了,会给你打个电话或者发个短信告诉你。
这就是异步IO,你可以同时进行其他操作,不用等IO操作完成。
再说说阻塞IO。
这个就像你去超市买东西,排了个长队等着结账。
你啥也干不了,就得等着。
这就是阻塞IO,你的程序会一直等着IO操作完成,啥也干不了。
非阻塞IO呢,就像你去自助结账机结账,不用排队。
你扫个码,如果商品没扫上,它会告诉你没扫上,你可以继续扫。
这就是非阻塞IO,你的程序可以立刻知道IO操作是否完成,如果没完成就继续尝试。
最后说说多路复用IO。
这个就像你去参加个自助餐会,桌子上摆了好多好吃的,你可以一边吃着这个,一边看着那个,想吃哪个就拿哪个。
这就是多路复用IO,你的程序可以同时监控多个IO操作,哪个完成了就处理哪个。
这五种IO模型,各有各的用处。
同步IO简单直接,异步IO灵活高效,阻塞IO稳妥但效率低,非阻塞IO响应快但可能忙碌,多路复用IO则能充分利用资源。
咱们得根据具体情况,选择合适的IO模型来吃饭,才能吃得开心,吃得饱饱的!。
Python并发编程中如何利用异步IO提高性能在当今的编程世界中,性能优化是一个永恒的话题。
特别是在处理大量并发任务时,如何提高程序的运行效率显得尤为重要。
Python 作为一种广泛使用的编程语言,在并发编程方面提供了多种方法,而异步 IO 就是其中一种强大的技术,可以显著提高程序的性能。
首先,我们来理解一下什么是异步 IO。
在传统的同步编程模式中,当一个任务执行 I/O 操作(如读取文件、网络请求等)时,程序会被阻塞,直到I/O 操作完成。
这意味着在这段时间内,CPU 处于空闲状态,浪费了宝贵的计算资源。
而异步 IO 则允许程序在发起 I/O 操作后,不必等待操作完成就可以继续执行其他任务,当 I/O 操作完成时,通过回调函数或其他机制通知程序进行后续处理。
那么,为什么异步 IO 能够提高性能呢?想象一下这样一个场景:我们有一个程序需要同时从多个网络源获取数据。
如果使用同步方式,程序会依次向每个源发送请求,然后等待每个请求的响应。
在等待响应的过程中,程序无法做其他事情。
但如果使用异步方式,程序可以同时发送所有请求,然后在等待响应的期间去处理其他任务,比如进行一些计算或者准备后续的数据处理逻辑。
这样,CPU 能够始终保持忙碌,充分利用其计算能力,从而大大提高了程序的整体效率。
```pythonimport asyncioimport aiofilesasync def read_file(file_path):async with aiofilesopen(file_path, mode='r') as f:content = await fread()print(f"Read content from {file_path}:{content}")async def main():tasks = read_file("file1txt"), read_file("file2txt"), read_file("file3txt")await asynciogather(tasks)asynciorun(main())```在上述示例中,`read_file` 函数是一个异步函数,用于读取文件的内容。
.NET异步编程:IO完成端口与BeginRead写这个系列原本的想法是讨论一下.NET中异步编程风格的变化,特别是F#中的异步工作流以及未来的.NET 5.0中的基于任务的异步编程模型。
但经过前几篇文章(为什么需要异步,传统的异步编程,使用CPS及yield实现异步)的发表后,很多人对IO异步背后实现的原理以及为什么这样能提高性能很感兴趣。
其实我本不想花更多的文字在这些底层实现的细节上,一来我并不擅长这些方面,二来我们使用.NET的异步IO就不需要关心这些底层东西,因为已经为你封装完备了。
不过为了避免大家一再在这上面商讨,我还是在这个系列中间插入了一篇来解释一下。
本文我将从内核对象IO完成端口开始介绍,然后来瞧瞧.NET BCL中的FileStream.BeginRead是如何利用IO完成端口来实现的。
IO完成端口(IO Completion Port)大多数人应该或多或少地听说过IO完成端口这么个东西,而且也知道它是实现高性能IO,高伸缩性应用的尚方宝剑。
IO完成端口是一个非常复杂的内核对象,其实现的也非常巧妙,细细琢磨还是非常有意思的。
创建高伸缩性的应用的一个基本原则就是:创建更少的线程。
线程数更少首先消耗的资源就少,每个线程的创建除了要浪费CPU时间外,还要创建一系列的数据结构用来保存线程相关的一些信息:用户栈,线程上下文,内核栈等。
这个总共加起来大概1.5M左右,那么你算算你的32位机器总共能使用多少内存?那么对应地能创建多少线程?可能有人讲那对于64位的就无所谓了。
嗯,在资源占用这方面64位确实不用担心。
但是系统中可运行的线程数越多,你的CPU数又是有限的(8个?80个?)。
Windows的任务调度机制是每个线程会运行一个时间片,然后Windows抢占式的调度另一个线程运行。
那么线程数越多,Windows势必要进行更频繁的线程上下文切换。
线程上下文切换对系统性能的影响在这里我就不多说了,你可以搜搜资料。
异步IO、APC、IO完成端口、线程池与高性能服务器之一异步IO背景:轮询 PIO DMA 中断早期IO设备的速度与CPU相比,还不是太悬殊。
CPU定时轮询一遍IO设备,看看有无处理要求,有则加以处理,完成后返回继续工作。
至今,软盘驱动器还保留着这种轮询工作方式。
随着CPU性能的迅速提高,这种效率低下的工作方式浪费了大量的CPU时间。
因此,中断工作方式开始成为普遍采用的技术。
这种技术使得IO设备在需要得到服务时,能够产生一个硬件中断,迫使CPU放弃目前的处理任务,进入特定的中断服务过程,中断服务完成后,再继续原先的处理。
这样一来,IO设备和CPU 可以同时进行处理,从而避免了CPU等待IO完成。
早期数据的传输方式主要是PIO(程控IO)方式。
通过对IO地址编程方式的方式来传输数据。
比如串行口,软件每次往串行口上写一个字节数据,串口设备完成传输任务后,将会产生一个中断,然后软件再次重复直到全部数据发送完成。
性能更好的硬件设备提供一个FIFO(先进先出缓冲部件),可以让软件一次传输更多的字节。
显然,使用PIO方式对于高速IO设备来说,还是占用了太多的CPU时间(因为需要通过CPU编程控制传输)。
而DMA(直接内存访问)方式能够极大地减少CPU处理时间。
CPU仅需告诉DMA控制器数据块的起始地址和大小,以后DMA控制器就可以自行在内存和设备之间传输数据,其间CPU可以处理其他任务。
数据传输完毕后将会产生一个中断。
同步文件IO和异步文件IO下面摘抄于MSDN《synchronous file I/O and asynchronous file I/O》。
有两种类型的文件IO同步:同步文件IO和异步文件IO。
异步文件IO也就是重叠IO。
在同步文件IO中,线程启动一个IO操作然后就立即进入等待状态,直到IO操作完成后才醒来继续执行。
而异步文件IO方式中,线程发送一个IO请求到内核,然后继续处理其他的事情,内核完成IO请求后,将会通知线程IO操作完成了。
如果IO请求需要大量时间执行的话,异步文件IO方式可以显著提高效率,因为在线程等待的这段时间内,CPU将会调度其他线程进行执行,如果没有其他线程需要执行的话,这段时间将会浪费掉(可能会调度操作系统的零页线程)。
如果IO请求操作很快,用异步IO方式反而还低效,还不如用同步IO 方式。
同步IO在同一时刻只允许一个IO操作,也就是说对于同一个文件句柄的IO操作是序列化的,即使使用两个线程也不能同时对同一个文件句柄同时发出读写操作。
重叠IO允许一个或多个线程同时发出IO请求。
异步IO在请求完成时,通过将文件句柄设为有信号状态来通知应用程序,或者应用程序通过GetOverlappedResult察看IO请求是否完成,也可以通过一个事件对象来通知应用程序。
异步IO、APC、IO完成端口、线程池与高性能服务器之二 APCAlertable IO(告警IO)提供了更有效的异步通知形式。
ReadFileEx / WriteFileEx在发出IO请求的同时,提供一个回调函数(APC过程),当IO请求完成后,一旦线程进入可告警状态,回调函数将会执行。
以下五个函数能够使线程进入告警状态:SleepExWaitForSingleObjectExWaitForMultipleObjectsExSignalObjectAndWaitMsgWaitForMultipleObjectsEx线程进入告警状态时,内核将会检查线程的APC队列,如果队列中有APC,将会按FIFO方式依次执行。
如果队列为空,线程将会挂起等待事件对象。
以后的某个时刻,一旦APC进入队列,线程将会被唤醒执行APC,同时等待函数返回WAIT_IO_COMPLETION。
QueueUserAPC可以用来人为投递APC,只要目标线程处于告警状态时,APC就能够得到执行。
使用告警IO的主要缺点是发出IO请求的线程也必须是处理结果的线程,如果一个线程退出时还有未完成的IO请求,那么应用程序将永远丢失IO完成通知。
然而以后我们将会看到IO完成端口没有这个限制。
下面的代码演示了QueueUserAPC的用法。
/************************************************************************//* APC Test. *//************************************************************************/DWORD WINAPI WorkThread(PVOID pParam){HANDLE Event = (HANDLE)pParam;for(;;){DWORD dwRet = WaitForSingleObjectEx(Event, INFINITE, TRUE);if(dwRet == WAIT_OBJECT_0)break;else if(dwRet == WAIT_IO_COMPLETION)printf("WAIT_IO_COMPLETION\n");}return 0;}VOID CALLBACK APCProc(DWORD dwParam){printf("%s", (PVOID)dwParam);}void TestAPC(BOOL bFast){HANDLE QuitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);HANDLE hThread = CreateThread(NULL,0,WorkThread,(PVOID)QuitEvent,0,NULL);Sleep(100); // Wait for WorkThread initialized.for(int i=5; i>0; i--){QueueUserAPC(APCProc, hThread, (DWORD)(PVOID)"APC here\n");if(!bFast)Sleep(1000);}SetEvent(QuitEvent);WaitForSingleObject(hThread, INFINITE);CloseHandle(hThread);}异步IO、APC、IO完成端口、线程池与高性能服务器之三 IO完成端口IO完成端口下面摘抄于MSDN《I/O Completion Ports》,smallfool翻译,原文请参考CSDN文档中心文章《I/O Completion Ports》,/Develop/article/29%5C29240.shtm。
I/O 完成端口是一种机制,通过这个机制,应用程序在启动时会首先创建一个线程池,然后该应用程序使用线程池处理异步I/O请求。
这些线程被创建的唯一目的就是用于处理I/O请求。
对于处理大量并发异步I/O请求的应用程序来说,相比于在I/O请求发生时创建线程来说,使用完成端口(s)它就可以做的更快且更有效率。
CreateIoCompletionPort函数会使一个I/O完成端口与一个或多个文件句柄发生关联。
当与一个完成端口相关的文件句柄上启动的异步I/O操作完成时,一个I/O完成包就会进入到该完成端口的队列中。
对于多个文件句柄来说,这种机制可以用来把多文件句柄的同步点放在单个对象中。
(言下之意,如果我们需要对每个句柄文件进行同步,一般而言我们需要多个对象(如:Event来同步),而我们使用IO Complete Port 来实现异步操作,我们可以同多个文件相关联,每当一个文件中的异步操作完成,就会把一个complete package放到队列中,这样我们就可以使用这个来完成所有文件句柄的同步)调用GetQueuedCompletionStatus函数,某个线程就会等待一个完成包进入到完成端口的队列中,而不是直接等待异步I/O请求完成。
线程(们)就会阻塞于它们的运行在完成端口(按照后进先出队列顺序的被释放)。
这就意味着当一个完成包进入到完成端口的队列中时,系统会释放最近被阻塞在该完成端口的线程。
调用GetQueuedCompletionStatus,线程就会将会与某个指定的完成端口建立联系,一直延续其该线程的存在周期,或被指定了不同的完成端口,或者释放了与完成端口的联系。
一个线程只能与最多不超过一个的完成端口发生联系。
完成端口最重要的特性就是并发量。
完成端口的并发量可以在创建该完成端口时指定。
该并发量限制了与该完成端口相关联的可运行线程的数目。
当与该完成端口相关联的可运行线程的总数目达到了该并发量,系统就会阻塞任何与该完成端口相关联的后续线程的执行,直到与该完成端口相关联的可运行线程数目下降到小于该并发量为止。
最有效的假想是发生在有完成包在队列中等待,而没有等待被满足,因为此时完成端口达到了其并发量的极限。
此时,一个正在运行中的线程调用GetQueuedCompletionStatus时,它就会立刻从队列中取走该完成包。
这样就不存在着环境的切换,因为该处于运行中的线程就会连续不断地从队列中取走完成包,而其他的线程就不能运行了。
对于并发量最好的挑选值就是您计算机中CPU的数目。
如果您的事务处理需要一个漫长的计算时间,一个比较大的并发量可以允许更多线程来运行。
虽然完成每个事务处理需要花费更长的时间,但更多的事务可以同时被处理。
对于应用程序来说,很容易通过测试并发量来获得最好的效果。
PostQueuedCompletionStatus函数允许应用程序可以针对自定义的专用I/O完成包进行排队,而无需启动一个异步I/O操作。
这点对于通知外部事件的工作者线程来说很有用。
在没有更多的引用针对某个完成端口时,需要释放该完成端口。
该完成端口句柄以及与该完成端口相关联的所有文件句柄都需要被释放。
调用CloseHandle可以释放完成端口的句柄。
下面的代码利用IO完成端口做了一个简单的线程池。
/************************************************************************/ /* Test IOCompletePort. */ /************************************************************************/DWORD WINAPI IOCPWorkThread(PVOID pParam){HANDLE CompletePort = (HANDLE)pParam;PVOID UserParam;WORK_ITEM_PROC UserProc;LPOVERLAPPED pOverlapped;for(;;){BOOL bRet = GetQueuedCompletionStatus(CompletePort,(LPDWORD)&UserParam,(LPDWORD)&UserProc,&pOverlapped,INFINITE);_ASSERT(bRet);if(UserProc == NULL) // Quit signal.break;// execute user's proc.UserProc(UserParam);}return 0;}void TestIOCompletePort(BOOL bWaitMode, LONG ThreadNum){HANDLE CompletePort;OVERLAPPED Overlapped = { 0, 0, 0, 0, NULL };CompletePort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,NULL,0);// Create threads.for(int i=0; i<ThreadNum; i++){HANDLE hThread = CreateThread(NULL,0,IOCPWorkThread,CompletePort,0,NULL);CloseHandle(hThread);}CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); BeginTime = GetTickCount();ItemCount = 20;for(i=0; i<20; i++){PostQueuedCompletionStatus(CompletePort,(DWORD)bWaitMode,(DWORD)UserProc1,&Overlapped);}WaitForSingleObject(CompleteEvent, INFINITE);CloseHandle(CompleteEvent);// Destroy all threads.for(i=0; i<ThreadNum; i++){PostQueuedCompletionStatus(CompletePort,NULL,NULL,&Overlapped);}Sleep(1000); // wait all thread exit.CloseHandle(CompletePort);}异步IO、APC、IO完成端口、线程池与高性能服务器之四线程池线程池下面摘抄于MSDN《Thread Pooling》。