首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

win32多线程程序设计札记(第六章下)

2013-09-29 
win32多线程程序设计笔记(第六章下)接着,我们讲解具体overlap I/O的使用。对于overlapped I/O的讨论,从简单

win32多线程程序设计笔记(第六章下)

接着,我们讲解具体overlap I/O的使用。对于overlapped I/O的讨论,从简单的应用开始,然后再演变到最高级的应用:

            激发的文件handles;        激发的Event对象;        异步过程调用(APCs);        I/O completion ports


    一、激发的文件Handles                


    以文件handle作为激发机制,熟悉一下异步过程:        1)打开文件,使用FILE_FLAG_OVERLAPPED作为参数指示为异步操作;        2)设置OVERLAPPED结构(进行设置)        3)调用读写操作,并将OVERLAPPED结构作为读写操作的最后一个参数
    下面引用书上的例子,来说明 overlapped:
    //反馈函数,供overlapped I/O操作完成时调用VOID WINAPI FileIOCompletionRoutine(DWORD dwErrorCode, //完成码                   DWORD dwNumberOfBytesTransfered,   // 被传递的字节数目                   LPOVERLAPPED lpOverlapped )        // 指向OVERLAPPED 结构的指针                 {    //这里运用了一个技巧,因为使用 APCs技术;那么OVERLAPPED 结构的event栏位没有什么用,可以用它来传递一些参数。    //在这里利用它传递序号,以表明是誰完成了 overlapped I/O    int nIndex = (int )(lpOverlapped->hEvent);        switch ( nIndex )   //针对nIndex ,做一些操作    {        case 1 :                               // 做一些操作。。。                 break;        case 2 :                 // 做一些操作。。。                 break;        //…    }           //如果所有overlapped I/O都处理完毕,将全局 event激发,使主程序结束    if (++nCompletionCount == MAX_REQUESTS)        SetEvent(ghEvent);}int main(){        ghEvent=CreateEvent(NULL,TRUE,FALSE, NULL );//构造全局event          ghFile = CreateFile( szPath,                      GENERIC_READ,                    FILE_SHARE_READ|FILE_SHARE_WRITE,                    NULL,                    OPEN_EXISTING,                    FILE_FLAG_OVERLAPPED,                    NULL     );               // 以overlapped 的方式打开文件         for (i=0; i<MAX_REQUESTS; i++){        //将同一文件按几个部分按 overlapped方式同时读,注意看QueueRequest函数是如何运做的        QueueRequest(i, i*16384, READ_SIZE);       }       // 等待所有操作完成       WaitForSingleObjectEx(ghEvent, INFINITE, TRUE );        CloseHandle(ghFile);       return EXIT_SUCCESS;}int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount){        gOverlapped[nIndex].hEvent = (HANDLE)nIndex; //记录overlapped 的序号    gOverlapped[nIndex].Offset = dwLocation;     //记录文件开始点        ReadFileEx(ghFile,gBuffers[nIndex],dwAmount,&gOverlapped[nIndex],FileIOCompletionRoutine);   //当读文件完成后自动调用FileIOCompletionRoutine}

              以上的三个程序片段都是对overlapped I/O具体运用;对付一般的I/O操作应是可以了,如果你需要更高的效率,那么你就需要书上提到的I/O Completion Ports了。 ===========================================

    下面完全转载:

    I/O Completion Ports也许是最好的方法了(看看书P172-P173你是否会有这种感觉呢?);但I/O Completion Ports 好像很难理解,我试着从自己理解的角度来写写心得(也许不对);描述:I/O Completion Ports看作容器,那么在这个容器中放置若干个线程(书上说最好是cpu个数*2+2),这些个线程随时随地的将被激活去服务I/O请求。 为什么I/O Completion Ports会很有效率?我是这样想的:首先容器中保持的线程可以随时承担服务I/O请求的任务,其主要特点是:两个线程在不同的时间可以服务同一个I/O请求;第二:这些个线程的调度由系统选择安排。系统总是在它认为最合适的时机去调度线程做最合适的事。书上还说了其它的一些有利于效率的工作,我就不再一一描述了。 下面我们来看一下运用I/O Completion Ports这是咱们最关心的: 在P179有一个操作概观:对着它,我来解释书上I/O Completion Ports 的例子。 //注意:一定要对着书,把下面用到的WIN32函数的每个参数的含义弄清楚,这很重要//――――――产生一个I/O completion port    ①     //构造一个I/O completion port     ghCompletionPort = CreateIoCompletionPort(        INVALID_HANDLE_VALUE,NULL, //不使用任何port0,      //这个参数用于在线程间传递参数,此时设为空            0       // 使用默认的线程数); //――――――让它和一个文件handle产生关联   ②     //将socket关联到①产生的I/O completion port,那么以后发生在这个socket//上的任何I/O操作,都由此I/O completion port中的线程处理CreateIoCompletionPort( (HANDLE)newsocket, //注意,这里是socket;原因见书P184    ghCompletionPort,  //指定的I/O completion port    (DWORD)pKey,   //一个指针,指向自定义的结构      0        // 使用默认的线程数      ); /* pKey,用于记录在newsocket上发生的操作,在这里指向一个自定义的结构,此结构中保存从客户端读入的数据和写回客户的数据*///――――――产生一堆线程    ③          CreateWorkerThreads(); //――――――下面是④、⑤步     //下面这段代码在线程函数ThreadFunc中for (;;){//――――――让每一个线程都在completion port上等待           ④        //下面这个函数相当于WaitForSingleObject;[提醒一下:这种等待不是忙等待]         //当一个线程读操作完成后,此函数返回          bResult = GetQueuedCompletionStatus(          ghCompletionPort, //指定在哪一个端口上等待          &dwNumRead,          &(DWORD) pCntx,     //一个指针,收到CreateIoCompletionPort所定义的key          &lpOverlapped,          INFINITE         );/*说明:pCntx就是CreateIoCompletionPort()中参数pkey;读出的数据被指定放入pkey->InBuffer中 ,仔细看看程序代码及p183的叙述,你就会发现在I/O Completion Ports中的线程是通过pkey来判别不同的客户端的*/          if (bResult==读完 )         { //将读出的数据保存到pCntx->OutBuffer中//注意:pCntx->nOutBufIndex记录着当前客户端已读出的数据的位置            char *pch = &pCntx->OutBuffer[pCntx->nOutBufIndex++];            *pch++ = pCntx->InBuffer[0];*pch = '/0';            if (pCntx->InBuffer[0] == '/n'){//如果读完了,将收到的信息写回客户端//――――――开始对着那个文件handle发出一些overlapped I/O请求     ⑤                WriteFile(                        (HANDLE)(pCntx->sock),                        pCntx->OutBuffer,                        pCntx->nOutBufIndex,                        &pCntx->dwWritten,                        &pCntx->ovOut                    );                pCntx->nOutBufIndex = 0;}//没有读完//――――――开始对着那个文件handle发出一些overlapped I/O请求     ⑤/*下面这一段我将书上的IssueRead(pCntx)拿掉了,替换它的是IssueRead(pCntx)函数的实现*///读操作,引发I/O completion port的操作          bResult = ReadFile(            (HANDLE)pCntx->sock,  //记取客户端的socket            pCntx->InBuffer,            //将从客户端的中读取的字符写入InBuffer            1,                          //每次读取一个字符            &numRead,            &pCntx->ovIn             //嘿!这家伙在这没啥用        );             } 上面程序片段没有列出“避免Completion Packets”的程序代码。当写入操作完成时,I/O Completion port将收到一个Packets以说明写入操作成功与否;如果写入操作的结果(成功或失败)不是很重要,那么我们肯定不希望在每次写入操作后都接收这样一个Packets(因为浪费时间),我要屏蔽它,可以向下面这样:     OVERLAPPED overlap;     //对hEvent向下面这样处理     overlap.hEvent = CreateEvent(…);     overlap.hEvent = (HANDLE)((DWORD)overlap.hEvent | 0x1);     //那么 “写操作”使用这个经过处理的 overlap后,I/O Completion port不会再发Packets     WriteFile(….&overlap);



热点排行