葡萄娱乐场在你身后变戏法

    这一章描述如何利用 overlapped I/O(也正是 asynchronous
I/O)。有个别时候 overlapped I/O 能够代替多线程的效益。不过,overlapped
I/O 加上completion ports,常被设计为二十三十二线程处理,以便在三个“受制于 I/O
的次序”(所谓 I/O bound 程序)中得到高效用。

    译注    深层探究 Win32 平台(WinNT 和 Win95)的 File Systems 和
Device I/O 的书本极少。Advanced Windows 3rd edition(杰夫rey
Richter/Microsoft Press)第 13章和第 14 章有无数珍惜的内容,可供参考。
  
 停止如今笔者曾经花了数章篇幅告诉各位“怎么样”以及“为啥”要利用线程。笔者将以这一章介绍1个你大概不想接纳八线程的场所。许多应用程序,例如终端机模拟程序,都需求在同权且间处理对三个之上的文书的读写操作。利用
Win32 所谓的 overlapped I/O 性子,你就能够让具备这个 I/O
操作并行处理,并且当其他四个 I/O
完毕时,你的次第会接收三个通报。别的操作系统把那么些特点称为 nonblocking
I/O 或 asynchronous I/O。
  
 回头看看第2章和第3章,那里已经示范怎么产生3个线程负责后台打字与印刷操作。事实上“后台打字与印刷”那种说法是不对的,大家在后台所进行的操作只可是是爆发打字与印刷机所需的多少。Windows
操作系统负责以打字与印刷管理器(Printer Manager)实现打字与印刷。打字与印刷管理器会
“spooled”
这一个准备好的数量,并且以打字与印刷机所能接受的速度,慢慢地将数据喂给打字与印刷机。
    关键在于 I/O 设备是个慢速设备,不论打字与印刷机、调制解调器,甚至硬盘,与
CPU 比较都奇慢无比。坐下来干等 I/O
的完毕是一件不甚明智的政工。有时候数据的流动率相当惊人,把数据从你的公文服务器中以
Ethernet
速度搬走,其速度只怕高达每秒一百万个字节。即使你尝试从文件服务器中读取100KB
,在用户的观点来看差不离是一念之差到位。但是,要知道,你的线程执行这几个命令,已经浪费了
10 个一百万次 CPU 周期。
    以后设想一下,同一个文本服务器,透过一条拨号线路,被 Remote Access
Services(RAS)处理。于是,100KB 的数据量在 ISDN 上急需 15 秒,在
9600bps
线路上需求大概两分钟。就算从用户的看法来看,八个窗口在那样长的光阴中完全没有反应,也是一定可怕的。
    这些题材的分明缓解方案正是,使用另二个线程来进展 I/O。不过,那就
    产生出部分有关难题,包含什么在主线程中操控许多少个 worker
线程、怎么样设定同步机制、怎样处理错误意况、怎么着展现对话框。这几个难点都将在本章出现并化解之。
    1个最简便易行的回答:overlapped I/O 是 Win32
的一项技艺,你能够须要操作系统为你传送数据,并且在传递达成时通报你。那项技术使您的顺序在I/O
进行进程中仍旧能够持续处监护人务。事实上,操作系统内部便是以线程来形成
overlapped I/O。你能够赢得线程的拥有利益,而不需提交什么样忧伤代价。
        重要!
        Windows 95 所支撑的 overlapped I/O 有个别限制,只适用于 named
pipes 、mailslots 、serial I/O 、以及 socket() 或 accept()
所传回来的sockets,它并不辅助磁盘或光盘中的文件操作。本章的装有例子只在Windows
NT 下才能够行得通运作。

    这一章对于 overlapped I/O
的研究,将从最简易的施用起来,然后再衍生和变化到最高级的运用。
        i 激发的文件 handles
        i 激发的 event 对象
        i 异步进度调用(Asynchronous Procedure Calls,APCs)
        i I/O completion ports
    个中以 I/O completion ports
尤其显得至关心珍视要,因为它们是唯一适用于高负荷服务器(必须同时保险广大总是线路)的贰个技巧。Completion
ports 利用部分线程,支持平衡由“I/O
请求”所引起的负荷。那样的架构尤其符合用在SMP 系统(译注:帮助四个 CPU
的操作系统)中发出所谓的 “scalable” 服务器。
    译注     所谓 scalable 系统,是指能够藉着增加 RAM 或磁盘空间或 CPU
个数而升级应用程序效率的一种系统。

    以文件 handle
作为激发体制,有一个了解的限定,那就是不能说出到底是哪二个 overlapped
操作实现了。假使种种文件 handle
唯有三个操作等待决定,上述难点莫过于并不成为难题。可是如小编稍早所说,系统有只怕还要接受数个操作,而它们都利用同一个文件
handle。于是很显明地,为每二个大概正在拓展中的 overlapped 操作调用
GetOverlappedResult(),并不是很有功能的做法。毫不令人惊讶,Win32
提供了三个相比较好的做法,用以缓解那样的标题。

    OVEPAJEROLAPPED 结构中的最后一个栏位,是三个 event handle。假使您选拔文件
handle 作为激发对象,那么此栏位可为 NULL。当那个栏位被设定为三个 event
对象时,系统焦点会在 overlapped 操作完成的时候,自动将此event
对象给激发起来。由于每一个 overlapped 操作都有它本人独一无二的OVEENVISIONLAPPED
结构,所以每三个结构都有它本人独一无二的多少个 event
对象,用以代表该操作。

    有一件事很重点:你所选取的 event
对象必须是手动重置(manual-reset)而非自动重置(auto-reset,详见第4章)。假设你使用电动重置,就恐怕发生出
race condition,因为系统主题有大概在你有时机等待该 event
对象在此之前,先激发它,而你精通,event
对象的鼓舞状态是不可能保留的(这或多或少和semaphore 分化)。于是这几个 event
状态将遗失,而你的 Wait…() 函数永不重临。可是贰个手动重置的
event,一旦激发,就一贯处于激发状态,直到你出手将它改变。
    
    使用 event 对象搭配 overlapped
I/O,你就能够对同一个文书发出七个读取操作和七个写入操作,每3个操作有协调的
event 对象;然后再调用WaitForMultipleObjects()
来等待之中之一(或任何)达成。

    来自 IOBYEVNT
的相干函数代码展现于列表6-2。在那之中的函数能够将1个一定的呼吁(并内定文件偏移值和数据大小)记录起来等待执行。

        列表 6-2 节录自IOBYEVNT.C—overlapped I/O with signaled
events

#0001 // Need to keep the events in their own array so we can wait on
them.
#0002 HANDLE ghEvents[MAX_REQUESTS];
#0003 // Keep track of each individual I/O operation
#0004 OVERLAPPED gOverlapped[MAX_REQUESTS];
#0005 // Handle to the file of interest.
#0006 HANDLE ghFile;
#0007 // Need a place to put all this data
#0008 char gBuffers[MAX_REQUESTS][READ_SIZE];
#0009
#0010 int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount)
#0011 {
#0012         int i;
#0013         BOOL rc;
#0014         DWORD dwNumread;
#0015         DWORD err;
#0016
#0017         MTVERIFY(
#0018         ghEvents[nIndex] = CreateEvent(
#0019                         NULL, // No security
#0020                         TRUE, // Manual reset – extremely
important!
#0021                         FALSE, // Initially set Event to
non-signaled state
#0022                         NULL // No name
#0023                     )
#0024         );
#0025         gOverlapped[nIndex].hEvent = ghEvents[nIndex];
#0026         gOverlapped[nIndex].Offset = dwLocation;
#0027
#0028         for (i=0; i<MAX_TRY_COUNT; i++)
#0029         {
#0030             rc = ReadFile(
#0031                     ghFile,
#0032                     gBuffers[nIndex],
#0033                     dwAmount,
#0034                     &dwNumread,
#0035                     &gOverlapped[nIndex]
#0036                 );
#0037
#0038             // Handle success
#0039             if (rc)
#0040             {
#0041                 printf(“Read #%d completed immediately.\n”,
nIndex);
#0042                 return TRUE;
#0043             }
#0044
#0045             err = GetLastError();
#0046
#0047             // Handle the error that isn’t an error. rc is zero
here.
#0048             if (err == ERROR_IO_PENDING)
#0049             {
#0050                 // asynchronous i/o is still in progress
#0051                 printf(“Read #%d queued for overlapped I/O.\n”,
nIndex);
#0052                 return TRUE;
#0053             }    
#0054
#0055             // Handle recoverable error
#0056             if ( err == ERROR_INVALID_USER_BUFFER ||
#0057                     err == ERROR_NOT_ENOUGH_QUOTA ||
#0058                     err == ERROR_NOT_ENOUGH_MEMORY )
#0059             {    
#0060                 Sleep(50); // Wait around and try later
#0061                 continue;
#0062             }
#0063
#0064             // Give up on fatal error.
#0065             break;
#0066         }
#0067
#0068         printf(“ReadFile failed.\n”);
#0069         return -1;
#0070 }
    
    你应当小心,那段代码为每3个“overlapped I/O 请求”产生3个新的event
对象,其 handle 存放在 OVERLAPPED 结构之中,也存放在三个大局组中,以便给
WaitForMultipleObjects() 使用。
    
    再壹遍笔者要告诉你,你可能会看出“I/O
请求”立即完毕的气象。上面正是一个其实的实践结果。

IOBYEVNT 的出口结果:

    Read #0 queued for overlapped I/O.
    Read #1 completed immediately.
    Read #2 completed immediately.
    Read #3 completed immediately.
    Read #4 completed immediately.
    QUEUED!!
    Read #0 return 1. 512 bytes were read.
    Read #1 return 1. 512 bytes were read.
    Read #2 return 1. 512 bytes were read.
    Read #3 return 1. 512 bytes were read.
    Read #4 return 1. 512 bytes were read.
    
    本例之中,Windows NT
在首先个“读入请求”之后,做了1个先行读取的操作(译注:约等于首先次读入时,多读一些多少),由此后续的“读入请求”马上完结并重回。
    
    你一定也来看了,ReadFile()
身处3个循环之中,由于操作系统的运行规律,你为 overlapped I/O
所钦点的缓冲区必须在内部存款和储蓄器中锁定。要是系统(或同1个先后中)有太多缓冲区在同一时半刻间被锁定,或然对效能是叁个十分大的有剧毒。由此,系统有时必须为顺序“温度下落”(下跌速度啦)。然则当然无法把它们阻塞住(blocking),因为那又使得
overlapped I/O 失去意义。Win32 会传回2个像 EENVISIONRO哈弗_INVALID_USER_BUFFE奇骏那样的错误信息,表示那前卫未丰盛的能源来处理这几个“I/O
请求”。那种题材也会生出在 IOBYFILE
程序中,只可是,为了让程序代码清爽一些,小编简单了对这么些错误新闻的拍卖。
    
    本章一伊始自作者曾建议如此的难点:以 Remote Access
Service(RAS)连接到八个服务器,原本 百分之十秒能够达成的数目搬移,今后内需两秒钟。那时候我们就能够把这一个操作记录起来,并做成
overlapped I/O,然后在主音讯循环中选拔 MsgWaitForMultipleObjects()
以便在获取数据后有所反应。