线程同步

 

 

 

16.3同步I/O

1.向设备读写数据的函数 为:ReadFile和WriteFile,

BOOL ReadFile(HANDLE hFile,LPVOID lpBuffer,

DWORD nNumberOfBytesToRead,

LPWORD lpNumberOfBytesRead,

LPOVERLAPPED lpOverlapped);

BOOL WriteFile(HANDLE hFile,CONST VOID * lpBuffer,

DWORD nNumberOfBytesToWrite,

LPWORD lpNumberOfBytesWrite,

LPOVERLAPPED lpOverlapped);


虽然这两个函数名字中都有File,但是它们都可以被用于任何设备。

参数说明:

hFile 是你想要访问的文件的句柄。

注意:当设备打开时,你不能使用 FILE_FLAG_OVERLAPPED标志,否则系统会认为你要异步访问该设备。

LpBuffer 用来指向读入数据的缓冲区或用于向文件中写入数据的缓冲区。

nNumberOfBytesToRead 和nNumberOfBytesToWrite分别告诉ReadFile和WriteFile从文件中读出和写入多少字节。

LpNumberOfBytesRead 和lpNumberOfBytesWrite分别给出一个DWORD的地址,函数将在其中添入成功地向设备传输的字节数。

lpOverlapped 在进行同步I/O时应当为NULL。

注意:只有设备是使用GENERIC_READ标志打开时,才可以使用 ReadFile。只有设备是使用GENERIC_WRITE标志打开时,才可以使用WriteFile。

2.向设备强制刷新数据:

使用函数FlushFileBuffer 来强制系统把缓存数据写回设备,

BOOL FlushFileBuffer(HANDLE hFile);

该函数强迫同参数hFile 标识的设备相关联的缓存数据写回设备。该设备必须使用GENERIC_WRITE打开。

16.4异步I/O

Win32 有四种技术用于异步I/O。下表简单总结了这几种技术,

技术

说明

使一个设备内核对象变为有信号

它允许一个线程发 出I/O请求,另一个线程进行处理。如果你要对单个设备进行多个同时的I/O请求,这种技术就没有用。

使一个事件内核对象变为有信号

允许对单个设备进 行多个同时的I/O请求。允许一个线程发出I/O请求,另一个线程进行处理。

警告I/O

允许对单个设备进 行多个同时的I/O请求。发出I/O请求的线程也必须处理它。

I/O完成端口

允许对单个设备进 行多个同时的I/O请求。允许一个线程发出I/O请求,另一个线程进行处理。该技术的申缩性和灵活性最好。

1. 使一个设备内核对象变为有信号

在异步I/O中要发出请 求,还得使用ReadFile和WriteFile函数,不过在它们的参数lpOverlapped必须被传递一个被初始化了的OVERLAPPED结构 指针。

Win32使用overlapped(重叠)指出花在执行I/O请求的时间与你的线程做其它事 情的时间重叠了。

当异步I/O时,调用ReadFile和WriteFile时,必须分配一个 OVERLAPPED结构,并初始化它的Offset、OffsetHigh和hEvent成员。当设备是文件时,Offset和OffsetHigh才 有用,对于其它设备,这两个成员必须被设置成0。

当访问文件时,Offset 和OffsetHigh指出了文件中你想要开始I/O操作的64位偏移量,例如:如果你想从一个文件的第123字节开始读取100个字节,可以这样做,

HANDLE hFile = CreateFile(…,FILE_FLAG_OVERLAPPED,…);

BYTE Buffer[100];

DWORD nNumBytesRead;

OVERLAPPED Overlapped;

Overlapped.Offset = 123;

Overlapped.OffsetHigh = 0;

Overlapped.hEvent = NULL;

ReadFile(hFile,bBuffer,sizeof(bBuffer),&nNumBytesRead,&Overlapped);

ReadFile和WriteFile 要求在每次异步I/O开始的字节必须在结构OVERLAPPED中指定。这与同步I/O是完全不同的。

Win32把设备句柄看作是可同步的对 象,即它或处于有信号状态,或处于无信号状态。当你调用ReadFile或WriteFile时,这些函数首先需要做的事情之一是将设备句柄重设置为无信 号状态。当所有数据都被读入或写入设备,系统就将设备句柄置为有信号状态。可以通过调用WaitForSingleObject或 WaitForMultpleObjects函数,线程就可以判断异步设备操作何时完成,即设备句柄何时被设置成有信号状态。也可以使用另外一个函数来达 到同前两个函数同样的效果,而且这个函数比前两个更好,

BOOL GetOverlappedResult(HANDLE hFile,

LPOVERLAPPED lpOverlapped,LPDWORD lpcbTransfer,BOOL fWait);

参数说明:

前两个参数必须是调用ReadFile 或WriteFile中的参数值。

lpcbTransfer 是成功传递的字节数。

FWait 表示GetOverlappedResult是否要等待重叠操作完成之后再返回。

2. 使一个事件内核对象变为有信号

在OVERLAPPED结构中有一个成员hEvent,它是 一个事件内核对象。如果你想同时执行多个异步设备I/O请求,你应该为每一个请求创建一个事件。初始化每一个请求的结构OVERLAPPED成员 hEvent,再调用ReadFile和WriteFile。当你到达了需要与I/O请求的完成同步的那一点时,只要调用 WaitForMultipleObjects,传递同每个I/O请求的OVERLAPPED结构相关联的事件句柄。这样,你就可以使用同一设备句柄,而 容易可靠地进行多个同时的异步设备I/O操作。

3. 使用警告I/O

当一个线程创建时,系统还给这个线程分配了一个队列,它叫异步过程调 用队列(APC,Asynchronous Procedure Call)。线程的异步过程调用队列类似于消息队列,不过消息队列是实现在“用户界面控件”上的消息,而异步过程调用队列是实现在“低层内核”,所以 APC队列执行的更有效也更快。

如何将消息发送到“APC”队列呢?调用ReadFileEx或WriteFileEx函数来发出I/O请求,这样I/O请求的结果就会被放到 APC队列中。还有另一个函数,它允许你人工的向APC队列中添加一项,

DWORD QueueUserAPC(PAPCFJNC pfnAPC,HANDLE hThread,DWORD dwData);

参数说明:

pfnAPC是一个APC函数的指针,该函数如下:VOID WINAPI APCFunc(DWORD dwParam);

hThread 是你想要添加表项的线程的句柄,注意它可以是系统中的任何线程,如果hThread是另一个进程中的线程,那么pfnAPC属于这个线程的进程。

DwData是传递给回调函数的一个32位值。

I/O请求结果放入APC队列后,线程并不是马上就能处理这些请求,如果要想这些请求可以被 马上处理,就必须使线程处于“警觉状态”,如何让线程处于警觉状态呢?可以使用下面这几个函数实现,

DWORD SleepEx(DWORD dwTimeOut,BOOL fAlertable);

DWORD WaitForSingleObjectEx(HANDLE hObject,DWORD dwTimeOut,BOOL fAlertable);

DWORD WaitForMultipleObjectsEx(DWORD cObjects,LPHANDLE lphObjects,BOOL fWaitAll,DWORD dwTimeOut,BOOL fAlertable);

DWORD SignalObjectAndWait(HANDLE hObjectToSignal,HANDLE hObjectToWaitOn,DWORD dwMilliseconds,BOOL bAlertable);

DWORD MsgWaitForMultipleObjectsEx(DWORD nCount,LPHANDLE pHandles,DWORD dwMilliseconds,DWORD dwWakeMask,DWORD dwFlags);

当调用上面五个函数之一使你的线程进入警觉状态时,系统首先检查线 程的APC队列,如果队列中至少有一项,系统就不会让线程休眠,系统会把该项从APC队列中取出来,你的线程调用 回调函数,传递给它完成的I/O请求的错误码,传输的字节数和OVERLAPPED结构的地址。当回调函数返回时,系统检查APC队列中是否有更多的项。 如果有,它们会被继续处理。如果没有函数调用就会返回。再说一下这五个函数的返回值,如果返回值是WAIT_IO_COMPLETION,那么线程的继续 执行就是因为线程的APC队列中至少有一个表项被处理了。如果返回值是别的,就说明线程被唤醒或者是因为睡眠超时了,或者是指定的内核对象变成有信号状态 了,或者是一个互斥对象被废弃

16.3 同步I/O 1.向设备读写数据的函数为:ReadFile和WriteFile, BOOL ReadFile(HANDLE hFile,LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); BOOL WriteFile(HANDLE hFile,CONST VOID * lpBuffer, DWORD nNumberOfBytesToWrite, LPWORD lpNumberOfBytesWrite, LPOVERLAPPED lpOverlapped); 虽然这两个函数名字中都有File,但是它们都可以被用于任何设备。 参数说明: hFile是你想要访问的文件的句柄。 注意:当设备打开时,你不能使用FILE_FLAG_OVERLAPPED标志,否则系统会认为你要异步访问该设备。 LpBuffer用来指向读入数据的缓冲区或用于向文件中写入数据的缓冲区。 nNumberOfBytesToRead和nNumberOfBytesToWrite分别告诉ReadFile和WriteFile从文件中读出和写 入多少字节。 LpNumberOfBytesRead和lpNumberOfBytesWrite分别给出一个DWORD的地址,函数将在其中添入成功地向设备传输的 字节数。 lpOverlapped在进行同步I/O时应当为NULL。 注意:只有设备是使用GENERIC_READ标志打开时,才可以使用ReadFile。只有设备是使用GENERIC_WRITE标志打开时,才可以使 用WriteFile。 2.向设备强制刷新数据: 使用函数FlushFileBuffer来强制系统把缓存数据写回设备, BOOL FlushFileBuffer(HANDLE hFile); 该函数强迫同参数hFile标识的设备相关联的缓存数据写回设备。该设备必须使用GENERIC_WRITE打开。 16.4异步I/O Win32有四种技术用于异步I/O。下表简单总结了这几种技术, 技术 说明 使一个设备内核对象变为有信号 它允许一个线程发出I/O请求,另一个线程进行处理。如果你要对单个设备进行多个同时的I/O请求,这种技术就没有用。 使一个事件内核对象变为有信号 允许对单个设备进行多个同时的I/O请求。允许一个线程发出I/O请求,另一个线程进行处理。 警告I/O 允许对单个设备进行多个同时的I/O请求。发出I/O请求的线程也必须处理它。 I/O完成端口 允许对单个设备进行多个同时的I/O请求。允许一个线程发出I/O请求,另一个线程进行处理。该技术的申缩性和灵活性最好。 1. 使一个设备内核对象变为有信号 在异步I/O中要发出请求,还得使用ReadFile和WriteFile函数,不过在它们的参数lpOverlapped必须被传递一个被初始化了的 OVERLAPPED结构指针。 Win32使用overlapped(重叠)指出花在执行I/O请求的时间与你的线程做其它事情的时间重叠了。 当异步I/O时,调用ReadFile和WriteFile时,必须分配一个OVERLAPPED结构,并初始化它的Offset、OffsetHigh 和hEvent成员。当设备是文件时,Offset和OffsetHigh才有用,对于其它设备,这两个成员必须被设置成0。 当访问文件时,Offset和OffsetHigh指出了文件中你想要开始I/O操作的64位偏移量,例如:如果你想从一个文件的第123字节开始读取 100个字节,可以这样做, HANDLE hFile = CreateFile(…,FILE_FLAG_OVERLAPPED,…); BYTE Buffer[100]; DWORD nNumBytesRead; OVERLAPPED Overlapped; Overlapped.Offset = 123; Overlapped.OffsetHigh = 0; Overlapped.hEvent = NULL; ReadFile(hFile,bBuffer,sizeof(bBuffer),&nNumBytesRead,&Overlapped); ReadFile和WriteFile要求在每次异步I/O开始的字节必须在结构OVERLAPPED中指定。这与同步I/O是完全不同的。 Win32把设备句柄看作是可同步的对象,即它或处于有信号状态,或处于无信号状态。当你调用ReadFile或WriteFile时,这些函数首先需要 做的事情之一是将设备句柄重设置为无信号状态。当所有数据都被读入或写入设备,系统就将设备句柄置为有信号状态。可以通过调用 WaitForSingleObject或WaitForMultpleObjects函数,线程就可以判断异步设备操作何时完成,即设备句柄何时被设置 成有信号状态。也可以使用另外一个函数来达到同前两个函数同样的效果,而且这个函数比前两个更好, BOOL GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped,LPDWORD lpcbTransfer,BOOL fWait); 参数说明: 前两个参数必须是调用ReadFile或WriteFile中的参数值。 lpcbTransfer是成功传递的字节数。 FWait表示GetOverlappedResult是否要等待重叠操作完成之后再返回。 2. 使一个事件内核对象变为有信号 在OVERLAPPED结构中有一个成员hEvent,它是一个事件内核对象。如果你想同时执行多个异步设备I/O请求,你应该为每一个请求创建一个事 件。初始化每一个请求的结构OVERLAPPED成员hEvent,再调用ReadFile和WriteFile。当你到达了需要与I/O请求的完成同步 的那一点时,只要调用WaitForMultipleObjects,传递同每个I/O请求的OVERLAPPED结构相关联的事件句柄。这样,你就可以 使用同一设备句柄,而容易可靠地进行多个同时的异步设备I/O操作。 3.使用警告I/O 当一个线程创建时,系统还给这个线程分配了一个队列,它叫异步过程调用队列(APC,Asynchronous Procedure Call)。线程的异步过程调用队列类似于消息队列,不过消息队列是实现在“用户界面控件”上的消息,而异步过程调用队列是实现在“低层内核”,所以 APC队列执行的更有效也更快。 如何将消息发送到“APC”队列呢?调用ReadFileEx或WriteFileEx函数来发出I/O请求,这样I/O请求的结果就会被放到APC队列 中。还有另一个函数,它允许你人工的向APC队列中添加一项, DWORD QueueUserAPC(PAPCFJNC pfnAPC,HANDLE hThread,DWORD dwData); 参数说明: pfnAPC是一个APC函数的指针,该函数如下:VOID WINAPI APCFunc(DWORD dwParam); hThead是你想要添加表项的线程的句柄,注意它可以是系统中的任何线程,如果hThread是另一个进程中的线程,那么pfnAPC属于这个线程的 进程。 DwData是传递给回调函数的一个32位值。 在I/O请求结果放入APC队列后,线程并不是马上就能处理这些请求,如果要想这些请求可以被马上处理,就必须使线程处于“警觉状态”,如何让线程处于警 觉状态呢?可以使用下面这几个函数实现, DWORD SleepEx(DWORD dwTimeOut,BOOL fAlertable); DWORD WaitForSingleObjectEx(HANDLE hObject,DWORD dwTimeOut,BOOL fAlertable); DWORD WaitForMultipleObjectsEx(DWORD cObjects,LPHANDLE lphObjects,BOOL fWaitAll,DWORD dwTimeOut,BOOL fAlertable); DWORD SignalObjectAndWait(HANDLE hObjectToSignal,HANDLE hObjectToWaitOn,DWORD dwMilliseconds,BOOL bAlertable); DWORD MsgWaitForMultipleObjectsEx(DWORD nCount,LPHANDLE pHandles,DWORD dwMilliseconds,DWORD dwWakeMask,DWORD dwFlags); 当调用上面五个函数之一使你的线程进入警觉状态时,系统首先检查线程的APC队列,如果队列中至少有一项,系统就不会让线程休眠,系统会把该项从APC队 列中取出来,你的线程调用回调函数,传递给它完成的I/O请求的错误码,传输的字节数和OVERLAPPED结构的地址。当回调函数返回时,系统检查 APC队列中是否有更多的项。如果有,它们会被继续处理。如果没有函数调用就会返回。再说一下这五个函数的返回值,如果返回值是 WAIT_IO_COMPLETION,那么线程的继续执行就是因为线程的APC队列中至少有一个表项被处理了。如果返回值是别的,就说明线程被唤醒或者 是因为睡眠超时了,或者是指定的内核对象变成有信号状态了,或者是一个互斥对象被废弃了。: Not Found
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值