zoukankan      html  css  js  c++  java
  • 第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. ", 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. ", 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. ");
    #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() 以便在取得数据后有所反应。

  • 相关阅读:
    爬虫入门(五)
    爬虫入门(四)
    爬虫入门(三)
    爬虫入门(二)
    爬虫入门(一)
    openpyxl的简单使用
    ansible(三)
    ansible(二)
    ansible(一)
    CF Global Round 10-F
  • 原文地址:https://www.cnblogs.com/azbane/p/7606515.html
Copyright © 2011-2022 走看看