第6章 Overlapped I/O, 在你身后变戏法 ---被激发的 Event 对象 -4
以文件 handle 作为激发机制,有一个明显的限制,那就是没办法说出到底是哪一个 overlapped 操作完成了。如果每个文件 handle 只有一个操作等待决定,上述问题其实并不成为问题。但是如我稍早所说,系统有可能同时接受数个操作,而它们都使用同一个文件 handle。于是很明显地,为每一个可能正在进行中的 overlapped 操作调用 GetOverlappedResult(),并不是很有效率的做法。毫不令人惊讶,Win32 提供了一个比较好的做法,用以解决这样的问题。
OVERLAPPED 结构中的最后一个栏位,是一个 event handle。如果你使用文件 handle 作为激发对象,那么此栏位可为 NULL。当这个栏位被设定为一个 event 对象时,系统核心会在 overlapped 操作完成的时候,自动将此event 对象给激发起来。由于每一个 overlapped 操作都有它自己独一无二的OVERLAPPED 结构,所以每一个结构都有它自己独一无二的一个 event 对象,用以代表该操作。
有一件事很重要:你所使用的 event 对象必须是手动重置(manual-reset)而非自动重置(auto-reset,详见第4章)。如果你使用自动重置,就可能产生出 race condition,因为系统核心有可能在你有机会等待该 event 对象之前,先激发它,而你知道,event 对象的激发状态是不能够保留的(这一点和semaphore 不同)。于是这个 event 状态将遗失,而你的 Wait...() 函数永不返回。但是一个手动重置的 event,一旦激发,就一直处于激发状态,直到你动手将它改变。
使用 event 对象搭配 overlapped I/O,你就可以对同一个文件发出多个读取操作和多个写入操作,每一个操作有自己的 event 对象;然后再调用WaitForMultipleObjects() 来等待其中之一(或全部)完成。
来自 IOBYEVNT 的相关函数代码显示于列表6-2。其中的函数能够将一个特定的请求(并指定文件偏移值和数据大小)记录起来等待执行。
列表 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 }
你应该注意,这段代码为每一个“overlapped I/O 请求”产生一个新的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 在第一个“读入请求”之后,做了一个预先读取的操作(译注:也就是第一次读入时,多读一些数据),因此后续的“读入请求”立刻完成并返回。
你一定也看到了,ReadFile() 身处一个循环之中,由于操作系统的运作原理,你为 overlapped I/O 所指定的缓冲区必须在内存中锁定。如果系统(或同一个程序中)有太多缓冲区在同一时间被锁定,可能对效率是一个很大的伤害。因此,系统有时候必须为程序“降温”(降低速度啦)。但是当然不能把它们阻塞住(blocking),因为那又使得 overlapped I/O 失去意义。Win32 会传回一个像 ERROR_INVALID_USER_BUFFER 那样的错误信息,表示此刻没有足够的资源来处理这个“I/O 请求”。这种问题也会发生在 IOBYFILE 程序中,只不过,为了让程序代码清爽一些,我省略了对这些错误信息的处理。
本章一开始我曾提出这样的问题:以 Remote Access Service(RAS)连接到一个服务器,原本 1/10 秒可以完成的数据搬移,现在需要两分钟。这时候我们就可以把这个操作记录起来,并做成 overlapped I/O,然后在主消息循环中使用 MsgWaitForMultipleObjects() 以便在取得数据后有所反应。
第6章 Overlapped I/O, 在你身后变戏法 ---被激发的 Event 对象 -4的更多相关文章
- 第6章 Overlapped I/O, 在你身后变戏法 ---被激发的 File Handles -3
最简单的 overlapped I/O 类型,是使用它自己的文件 handle 作为同步机制.首先你以 FILE_FLAG_OVERLAPPED 告诉 Win32 说你不要使用默认的同步 I/O.然后 ...
- 第6章 Overlapped I/O, 在你身后变戏法 ---1
这一章描述如何使用 overlapped I/O(也就是 asynchronous I/O).某些时候 overlapped I/O 可以取代多线程的功用.然而,overlapped I/O 加上co ...
- 第6章 Overlapped I/O, 在你身后变戏法 ---Win32 文件操作函数 -2
Win32 之中有三个基本的函数用来执行 I/O,它们是: i CreateFile() i ReadFile() i WriteFile() 没有另外 ...
- 第二章——Serializable的使用(跨进程使用和Intent的传递对象)
一.Serializable类(JAVA本身具有的) 简介:Serializable是一个接口. 作用:是JAVA提供的序列化接口,实现序列化和反序列化的操作. 二.跨进程使用 1.事前准备 publ ...
- 第4章 同步控制 Synchronization ----同步机制的摘要
同步机制摘要Critical Section Critical section(临界区)用来实现"排他性占有".适用范围是单一进程的各线程之间.它是: 一个局部性对象,不是一个核 ...
- [Java编程思想-学习笔记]第2章 一切都是对象
2.1 创建新的数据类型:类 通过第一章掌握了面向对象的理论后,我们知道每个对象必定属于一个类型,那么Java如何创建新的数据类型?如下程序所示: class Circle { // 属性 // 方 ...
- 读《编写可维护的JavaScript》第七章总结
第七章 事件处理 7.1 典型用法 作者首先给了个我们一个处理事件的方法.看起来也没啥俩样,不过后来给出的优化方法很值得学习: // 不好的写法 function handleClick(even ...
- 第十一章:WEB浏览器中的javascript
客户端javascript涵盖在本系列的第二部分第10章,主要讲解javascript是如何在web浏览器中实现的,这些章节介绍了大量的脚本宿主对象,这些对象可以表示浏览器窗口.文档树的内容.这些章节 ...
- 第十六章:脚本化HTTP
写在本章内容前: 第十五章:事件处理 涉及到到较多的文字篇幅,介于个人精力问题,暂不更新.主要包含的内容有事件类型.注册事件处理程序.事件处理程序的调用.文档加载事件.鼠标事件.鼠标滚轮事件.拖放事件 ...
随机推荐
- 谈谈分布式版本管理工具Git
一.主流的版本管理工具 目前在企业中比较主流的版本管理工具有:GIT.SVN.CVS等等. 二.什么是Git? Git是一款免费.开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目.是 L ...
- ubuntu 14.04中安装phpmyadmin即mysql图形管理界面
由于学习的需要,我将网站开发环境从windows转移到了ubuntu,ubuntu下之前并没有发现什么难的地方,只要百度一般都有解决方案.但是总所周知ubuntu是一系列开源软件的集合,由于版本的问题 ...
- 【小白成长撸】--链栈(C语言版)
// 链栈.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <stdio.h> #include <st ...
- JavaScript: 使用 atan2 来绘制 箭头 和 曲线
最近搞Canvas绘图,知道了JavaScript中提供了atan2(y,x)这样一个三角函数.乍眼一看,不认识,毕竟在高中时,学过的三角函数有:sin,cos,arcsin,arccos,tan,a ...
- Windbg DUMP
Windbg DUMP分析(原创汇总) 1. 引入篇 1.1 下载安装 1.2 调试器 1.3 操作界面2. 命令篇 2.1 按照来源划分 2.1.1 基本命令 2.1.2 元命令 2.1.3 扩展命 ...
- css3 如何实现圆边框的渐变
使用 css 实现下面效果: 把效果分解. 代码一: <style> .helper1 { height: 40px; padding: 15px; background: -webkit ...
- Spring Ioc DI 原理
IOC(DI):其实这个Spring架构核心的概念没有这么复杂,更不像有些书上描述的那样晦涩.Java程序员都知道:java程序中的每个业务逻辑至少需要两个或以上的对象来协作完成,通常,每个对象在使用 ...
- 201521123066《Java程序设计》第七周学习总结
第7周-集合 本周学习总结 其他注意点: (1)List主要特征: 其元素以线性方式存储 集合中允许存放重复对象 (2)Set中的对象: 不按特定方式排序 没有重复对象 Set最多有一个null元素 ...
- 201521123015 《Java程序设计》第2周学习总结
1.本章学习总结 (1)学习了枚举,数组等方法 (2)通过实验内容的讲解,解决了一些问题 (3)进一步运用和了解码云 书面作业 1.使用Eclipse关联jdk源代码,并查看String对象的源代码( ...
- 201521123006 《java程序设计》 第13周学习总结
1. 本周学习总结 1.以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.j ...