zoukankan      html  css  js  c++  java
  • 多线程笔记5

    第六章:Overlapped I/O,在你身后变戏法

    1.overlapped I/O 是 Win32 的一项技术,你可以要求操作系统为你传送数据,并且在传送完毕时通知你。这项技术使你的程序在I/O 进行过程中仍然能够继续处理事务。事实上,操作系统内部正是以线程来完成 overlapped I/O。

    2.Win32文件操作函数

    (1)CreateFile()可以用来打开文件、串行口和并行口、Named pipes、Console。

    HANDLE CreateFile(
    LPCTSTR lpFileName,                                         // 指向文件名称
    DWORD dwDesiredAccess,                                  // 存取模式(读或写)
    DWORD dwShareMode,                                      // 共享模式(share mode)
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,  // 指向安全属性结构
    DWORD dwCreationDisposition,                          // 如何产生
    DWORD dwFlagsAndAttributes,                          // 文件属性
    HANDLE hTemplateFile                                      // 一个临时文件,将拥有全部的属性拷贝
    );

    注:overlapped I/O性质:可以在同一时间读写文件的许多部分;多个overlapped请求,执行次序无法保证;overlapped I/O的基本型式是以ReadFile()和WriteFile()完成的。

    (2)ReadFile()

    BOOL ReadFile(
    HANDLE hFile,                                     // 欲读之文件
    LPVOID lpBuffer,                                 // 接收数据之缓冲区
    DWORD nNumberOfBytesToRead,         // 欲读取的字节个数
    LPDWORD lpNumberOfBytesRead,        // 实际读取的字节个数的地址
    LPOVERLAPPED lpOverlapped               // 指针,指向 overlapped info
    );

    (3)WriteFile()

    BOOL WriteFile(
    HANDLE hFile,                                   // 欲写之文件
    LPCVOID lpBuffer,                             // 储存数据之缓冲区
    DWORD nNumberOfBytesToWrite,       // 欲写入的字节个数
    LPDWORD lpNumberOfBytesWritten,   // 实际写入的字节个数的地址
    LPOVERLAPPED lpOverlapped             // 指针,指向 overlapped info
    );

    如果CreateFile() 的第6个参数被指定为FILE_FLAG_ OVERLAPPED,就必须在上述的 lpOverlapped 参数中提供一个指针,指向一个 OVERLAPPED 结构。

    (4)OVERLAPPED结构

    typedef struct _OVERLAPPED {
    DWORD Internal;        //通常保留。当GetOverlappedResult传回False,并且GetLastError并非传回ERROR_IO_PENDING,则内含一个视系统而定的状态。
    DWORD InternalHigh; //通常被保留,当GetOverlappedResult传回True,则内含“被传输数据的长度”
    DWORD Offset;          //读、写偏移位置,从文件头开始算起。若目标设备不支持文件位置,忽略
    DWORD OffsetHigh;   //64位文件偏移中较高32位。若目标设备不支持文件位置,忽略
    HANDLE hEvent;       //manual reset event,overlapped I/O完成时被激发。ReadFileEX,WriteFileEX忽略这个栏位,彼时被用来传递一个用户自定义的指针。
    } OVERLAPPED, *LPOVERLAPPED;

    OVERLAPPED 结构执行两个重要的功能。第一,它像一把钥匙,用以识别每一个目前正在进行的 overlapped 操作。第二,它在你和系统之间提供了一个共享区域,参数可以在该区域中双向传递。

    通常overlapped结构存放在heap中。

    3.被激发的File Handles

    (1)异步IO的步骤:CreateFile指定FILE_FLAG_OVERLAPPED;设立一个OVERLAPPED结构,调用ReadFile、WriteFile带上这个参数。

    (2)文件handle是一个核心对象,一旦操作完毕即被激发。

    (3)GetOverlappedResult()

    BOOL GetOverlappedResult(
    HANDLE hFile,                                        //文件设备的handle
    LPOVERLAPPED lpOverlapped,                 //一个指针,指向overlapped结构
    LPDWORD lpNumberOfBytesTransferred,  //一个指针,指向DWORD,保存真正被传输的字节数。
    BOOL bWait                                          //是否要等待操作完成,TRUE表示等待。
    );

    (4)虽然你要求一个overlapped 操作,但它并不一定就是 overlapped!如果数据已经被放进 cache中,或如果操作系统认为它可以很快速地取得那份数据,那么文件操作就会在ReadFile() 返回之前完成,而 ReadFile() 将传回 TRUE。

    (5)一个文件操作为 overlapped,而操作系统把“操作请求”放到队列中等待执行, ReadFile() 和、WriteFile()都会传回 FALSE 以示失败。这个行为并不是很直观, 你必须调用GetLastError() 并确定它传回 ERROR_IO_PENDING,那意味着“overlappedI/O 请求”被放进队列之中等待执行。GetLastError() 也可能传回其他的值,例如 ERROR_HANDLE_EOF,那就真正代表一个错误了。

    4.被激发的event对象

    (1)所使用的 event 对象必须是手动重置(manual-reset)而非自动重置(auto-reset)。

    (2)IOBYEVENT例子。

    5.异步过程调用(Asynchronous Procedure Calls,APCs)

    (1)使用overlapped I/O与event搭配的两个问题:

    <1>WaitForMultipleObjects最多等待64个对象。

    <2>必须不断的根据“哪一个handle被激发”而计算如何反应。

    (2)使用Ex版的ReadFile和WriteFile,可以使用异步过程调用机制。只有当线程处于alertable状态时,APCs才会被调用。当线程因为以下5个函数而处于等待状态,且线程的“alertable”标记被设为TRUE,则线程处于alertable状态:

    SleepEx()

    WaitForSingleObjectEx()

    WaitForMultipleObjectEx()

    MsgWaitForMultipleObjectsEx()

    SignalObjectAndWait();

    (3)用于 overlapped I/O 的 APCs 是一种所谓的 user mode APCs。WindowsNT 另有一种所谓的 kernel mode APCs。Kernel mode APCs 也会像 usermode APCs 一样被保存起来,但一个 kernel mode APC 一定会在下一个timeslice 被调用,不管线程当时正在做什么。 Kernel mode APCs 用来处理系统机能,不在应用程序的控制之中。

    (4)提供的 I/O completion routine 应该有这样的型式:

    VOID WINAPI FileIOCompletionRoutine(
    DWORD dwErrorCode,   //0表示操作完成,ERROR_HANDLE_EOF表示操作已经到了文件尾端。
    DWORD dwNumberOfBytesTransferred,//真正被传输的数据字节数
    LPOVERLAPPED lpOverlapped//指向overlapped结构,此结构由开启overlapped I/O操作的函数提供
    );

    (5)使用 APCs 时,OVERLAPPED 结构中的 hEvent 栏位不需要用来放置一个 event handle。Win32 文件上说此时 hEvent 栏位可以由程序员自由运用。那么最大的用途就是:首先配置一个结构,描述数据来自哪里,或是要对数据进行一些什么操作,然后将 hEvent 栏位设定指向该结构

    (6)在C++ 中产生一个I/O Completion Routines:储存一个指针,指向用户自定义数据(一个对象),然后经由此指针调用一个 C++ 成员函数。由于 static 成员函数是类的一部分,你还是可以调用 private 成员函数。

    6.对文件进行overlapped I/O的缺点

    (1)似乎 Windows NT 是以“I/O 请求”的大小来决定要不要将此请求先记录下来。所以对于数据量小的操作,overlapped I/O的效率反而更低。

    (2)解决办法:以少量的线程负责所有的硬盘 I/O,然后把这些线程的I/O 请求,保持在一个队列之中。这种效率比较高。

    (3)有两种情况,overlapped I/O 总是同步执行,甚至即使 FILE_FLAG_NO_BUFFERING 已经指定。第一种情况是你进行一个写入操作而造成文件的扩展。第二种情况是你读写一个压缩文件。

    7.I/O Completion Ports

    (1)APCs的缺点:最大的问题就是,有好几个 I/O APIs 并不支持 APCs,如listen() 和 WaitCommEvent() 便是两个例子。APCs 的另一个问题是,只有发出“overlapped 请求”的那个线程才能够提供 callback 函数,然而在一个“scalable”(译注)系统中,最好任何线程都能够服务 events。

    (2)产生一个I/O Completion Port

    HANDLE CreateIoCompletionPort(
    HANDLE FileHandle,                        //文件或设备的handle,若为INVALID_HANDLE_VALUE,则产生一个没有和任何handle关联的port。
    HANDLE ExistingCompletionPort,      //若此栏位被指定,则FileHandle被加到此port上。指定Null产生一个新的port。
    DWORD CompletionKey,                  //用户自定义的一个数值,将被交给提供服务的线程。此值和FileHanlde有关联。
    DWORD NumberOfConcurrentThreads//与此I/O completion port 有关联的线程个数。
    );

    (3)与一个文件handle产生关联

      再次使用CreateIoCompletionPort接口。

    (4)在一个I/O Completion Port上等待

    BOOL GetQueuedCompletionStatus(
    HANDLE CompletionPort,                        //将在其上等待completion port。
    LPDWORD lpNumberOfBytesTransferred,  //指向DWORD,收到“被传输的数据字节数”。
    LPDWORD lpCompletionKey,                   //指向DWORD,该DWORD将收到由CreateIoCompletionPort定义的key。
    LPOVERLAPPED *lpOverlapped,               //overlapped结构指针的地址。
    DWORD dwMilliseconds                          //等待的最长时间,时间终了,lpOverlapped被设为NULL,函数传回FALSE。
    );

    在completion port上等待的线程是以先进后出的次序提供服务。

    (5)避免Completion Packets

    设定一个 OVERLAPPED 结构,内含一个合法的手动重置(manual-reset)event 对象,放在 hEvent 栏位。然后把该 handle 的最低位设为 1。

    overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    overlap.hEvent = (HANDLE)((DWORD)overlap.hEvent | 0x1);

    WriteFile(hFile, buffer, 128,& dwBytesWritten, &overlap);

    8.对Sockets使用Overlapped I/O

    分析ECHO例子,多实践socket IOCP。

  • 相关阅读:
    归并排序
    汉诺塔系列问题: 汉诺塔II、汉诺塔III、汉诺塔IV、汉诺塔V、汉诺塔VI、汉诺塔VII
    Uncle Tom's Inherited Land
    汉诺塔III
    汉诺塔X
    Frosh Week
    hdu 1007最近点对问题
    POJ1579:Function Run Fun
    Hdu1163 Eddy's digitai Roots(九余数定理)
    放苹果问题
  • 原文地址:https://www.cnblogs.com/programmer-wfq/p/4646151.html
Copyright © 2011-2022 走看看