kenlistian

厚积薄发. 勤为桨,思为帆

   :: 首页 :: 新随笔 ::  :: 聚合  :: 管理 ::
  73 随笔 :: 4 文章 :: 22 评论 :: 0 Trackbacks

#

window32下的进程通讯总结

1。内存映射文件

方法

  a。创建  在发送数据的进程中调用CreateFileMapping创建有名的共享内存:
HANDLE CreateFileMapping(
      HANDLE hFile,    // 映射文件的句柄,
                                       //设为0xFFFFFFFF以创建一个进程间共享的对象
     LPSECURITY_ATTRIBUTES lpFileMappingAttributes,    // 安全属性
     DWORD flProtect,                                   // 保护方式
     DWORD dwMaximumSizeHigh,            //对象的大小
    DWORD dwMaximumSizeLow,   
    LPCTSTR lpName                                    // 必须为映射文件命名
);
  保护方式可以是PAGE_READONLY或是PAGE_READWRITE。如果多进程都对同一共享内存进行写访问,则必须保持相互间同步。
   映射文件还可以指定PAGE_WRITECOPY标志,可以保证其原始数据不会遭到破坏。

HANDLE hMySharedMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF), NULL,
                                                             PAGE_READWRITE,0,0x1000,"MySharedMem"); 

b。调用MapViewOfFile函数映射到本进程的地址空间内。
   LPSTR pszMySharedMapView=(LPSTR)MapViewOfFile(hMySharedMapFile,
                                                            FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

C。访问。其他进程访问共享对象,获得对象名并调用OpenFileMapping函数。
    HANDLE hMySharedMapFile=OpenFileMapping(FILE_MAP_WRITEFALSE,"MySharedMem");
      一旦其他进程获得映射对象的句柄,可以象创建进程那样调用MapViewOfFile函数来映射对象视图。用户可以使用该对象视图来进行数据读写操作,以达到数据通讯的目的。

d。结束使用共享内存后,调用UnmapViewOfFile函数以取消其地址空间内的视图:
    if (!UnmapViewOfFile(pszMySharedMapView))
   {         AfxMessageBox("could not unmap view of file"); }

2。共享内存DLL

   在VC中使用data_seg pragma宏。如下
       #pragma data_seg("MYSEC")
           char MySharedData[4096]={0};         //注意初始化
        #pragma data_seg()
   然后在用户的DEF文件中为有名的数据区设定共享属性。
   LIBRARY TEST
             DATA READ WRITE
   SECTIONS
            .MYSEC READ WRITE SHARED

这样每个附属于DLL的进程都将接受到属于自己的数据拷贝,一个进程的数据变化并不会反映到其他进程的数据中。

在DEF文件中适当地输出数据。以下的DEF文件项说明了如何以常数变量的形式输出MySharedData。
EXPORTS
    MySharedData CONSTANT
最后在应用程序(进程)按外部变量引用共享数据。
extern _export"C"{ char * MySharedData[];}
  进程中使用该变量应注意间接引用。
m_pStatic=(CEdit*)GetDlgItem(IDC_SHARED);
m_pStatic->GetLine(0,*MySharedData,80);

3。WM_COPYDATA

   WM_COPYDATA消息主要目的是允许在进程间传递只读数据。SDK文档推荐用户使用SendMessage函数,接受方在数据拷贝完成前不返回,这样发送方就不可能删除和修改数据:

SendMessage(hwnd,WM_COPYDATA,wParam,lParam);
其中wParam设置为包含数据的窗口的句柄。lParam指向一个COPYDATASTRUCT的结构:
typedef struct tagCOPYDATASTRUCT{
             DWORD dwData;       //用户定义数据
            DWORD cbData;        //数据大小
            PVOID lpData;           //指向数据的指针
       }COPYDATASTRUCT;


在实际应用中进程之间需要发送和接收Windows消息来通知进程间相互通讯,发送方发送通讯的消息以通知接收方,接收方在收到发送方的消息后就可以对内存进行读写操作。
    可以在程序设计中采用Windows注册消息进行消息传递,首先在发送进程初始化过程中进行消息注册:
m_nMsgMapped=::RegisterWindowsMessage("Mapped");
m_nMsgHandle=::RegisterWindowsMessage("Handle");
m_nMsgShared=::RegisterWindowsMessage("Shared");
在程序运行中向接收进程发送消息:
CWnd* pWndRecv=FindWindow(lpClassName,"Receive");
pWndRecv->SendMessage(m_MsgMapped,0,0);
pWndRecv->SendMessage(m_nMsgHandle,
(UINT)GetCurrentProcessID(),
(LONG)pApp->m_hGlobalHandle);
pWndRecv->SendMessage(m_nMsgShared,0,0);

可以按如下方式发送WM_COPYDATA消息:
static COPYDATASTRUCT cds;                 //用户存放数据
pWnd->SendMessage(WM_COPYDATA,NULL,(LONG)&cds);

接收方进程初始化也必须进行消息注册:
UNIT CRecvApp:: m_nMsgMapped=::RegisterWindowsMessage("Mapped");
UNIT CRecvApp::m_nMsgHandle=::RegisterWindowsMessage("Handle");
UNIT CRecvApp::m_nMsgShared=::RegisterWindowsMessage("Shared");
同时映射消息函数如下:
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgMapped,OnRegMsgMapped)
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgHandle,OnRegMsgHandle)
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgShared,OnRegMsgShared)
在这些消息函数我们就可以采用上述技术实现接收进程中数据的读写操作了。 

 4。调用ReadProcessMemory以及WriteProcessMemory函数.

  调用ReadProcessMemory以及WriteProcessMemory函数用户在发送进程中分配一块内存存放数据,调用GlobalAlloc或VirtualAlloc函数实现:
pApp->m_hGlobalHandle=GlobalAlloc(GMEM_SHARE,1024);
可以得到指针地址:
pApp->mpszGlobalHandlePtr=(LPSTR)GlobalLock
(pApp->m_hGlobalHandle);
在接收进程中要用到用户希望影响的进程的打开句柄。为了读写另一进程,按如下方式调用OpenProcess函数:
HANDLE hTargetProcess=OpenProcess(
      STANDARD_RIGHTS_REQUIRED|
      PROCESS_VM_REDA|
      PROCESS_VM_WRITE|
      PROCESS_VM_OPERATION,//访问权限
      FALSE,//继承关系
     dwProcessID);//进程ID
为保证OpenProcess函数调用成功,用户所影响的进程必须由上述标志创建。
用户获得一个进程的有效句柄,就可调用ReadProcessMemory函数读取该进程的内存:
BOOL ReadProcessMemory(
             HANDLE hProcess,    // 进程指针
             LPCVOID lpBaseAddress,    // 数据块的首地址
             LPVOID lpBuffer,    // 读取数据所需缓冲区
            DWORD cbRead,    // 要读取的字节数 
            LPDWORD lpNumberOfBytesRead    
);
   使用同样的句柄也可以写入该进程的内存:
BOOL WriteProcessMemory(
           HANDLE hProcess,    // 进程指针
           LPVOID lpBaseAddress,    // 要写入的首地址
           LPVOID lpBuffer,    // 缓冲区地址
           DWORD cbWrite,    // 要写的字节数
           LPDWORD lpNumberOfBytesWritten
    );   

posted @ 2006-05-28 00:29 kenlistian 阅读(444) | 评论 (0)编辑 收藏

 

我一直想把ado封装在一个dll中,看到一般常用的在stdafx。h中加入

这样的一句,

#import "c:\program files\common files\system\ado\msado15.dll"no_namespaces rename("EOF" adoEOF")

如果在mfc中生成的dll中也添加这一句,将不会编译通过,有很多命名错误,可以改成如下的导入

#pragma warning(disable:4146)
#import "c:\program files\common files\system\ado\msado15.dll" named_guids rename("EOF","adoEOF"), rename("BOF","adoBOF")
#pragma warning(default:4146)

即可通过,然后和一般在mfc主程序调用ado一样。

 

posted @ 2006-05-27 14:35 kenlistian 阅读(178) | 评论 (0)编辑 收藏

 

从网上整理的文章,同样,这只是为了我增加理解记忆而做到得笔记,
不存在利用价值,纯粹是学习和记忆.抄袭也好学习也好只是让人明
白道理.主要干活的还是自己的程序.

I/O设备处理必然让主程序停下来干等I/O的完成,
对这个问题有

方法一:使用另一个线程进行I/O。这个方案可行,但是麻烦。

方法二:使用overlapped I/O。
  正如书上所说:“overlapped I/O是WIN32的一项技术,
    你可以要求操作系统为你传送数据,并且在传送完毕时通知你。
    这项技术使你的程序在I/O进行过程中仍然能够继续处理事务。
    事实上,操作系统内部正是以线程来I/O完成overlapped I/O。
    你可以获得线程的所有利益,而不需付出什么痛苦的代价”。
   

怎样使用overlapped I/O:

进行I/O操作时,指定overlapped方式
使用CreateFile (),将其第6个参数指定为FILE_FLAG_OVERLAPPED,
就是准备使用overlapped的方式构造或打开文件;
如果采用 overlapped,那么ReadFile()、WriteFile()的第5个参数必须提供一个指针,
指向一个OVERLAPPED结构。 OVERLAPPED用于记录了当前正在操作的文件一些相关信息。

//功能:从指定文件的1500位置读入300个字节
int main()
{
    BOOL rc;
    HANDLE hFile;
    DWORD numread;
    OVERLAPPED overlap;
    char buf[512];
    char szPath=”x:\\xxxx\xxxx”;
   
    //检查系统,确定是否支持overlapped,(NT以上操作系统支持OVERLAPPED)
    CheckOsVersion();
    // 以overlapped的方式打开文件
    hFile = CreateFile( szPath,
                    GENERIC_READ,
                    FILE_SHARE_READ|FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_FLAG_OVERLAPPED,
                    NULL
                );

    // OVERLAPPED结构实始化为0
    memset(&overlap, 0, sizeof(overlap));
    //指定文件位置是1500;
    overlap.Offset = 1500;
   
    rc = ReadFile(hFile,buf,300,&numread,&overlap);
    //因为是overlapped操作,ReadFile会将读文件请求放入读队列之后立即返回(false),
    //而不会等到文件读完才返回(true)
    if (rc)
    {
       //文件真是被读完了,rc为true
       // 或当数据被放入cache中,或操作系统认为它可以很快速地取得数据,rc为true
    }
    else
    {
        if (GetLastError() == ERROR_IO_PENDING)
        {//当错误是ERROR_IO_PENDING,那意味着读文件的操作还在进行中
         //等候,直到文件读完
            WaitForSingleObject(hFile, INFINITE);
            rc = GetOverlappedResult(hFile,&overlap,&numread,FALSE);
            //上面二条语句完成的功能与下面一条语句的功能等价:
            // GetOverlappedResult(hFile,&overlap,&numread,TRUE);
         }
         else
         {
            //出错了
        }
    }
    CloseHandle(hFile);
    return EXIT_SUCCESS;
}

在实际工作中,若有几个操作同一个文件时,
怎么办?我们可以利用OVERLAPPED结构中提供的event来解决上面遇到的问题。
注意,你所使用的event对象必须是一个MANUAL型的;否则,可能产生竞争条件。
原因见书P159。
int main()
{
    int i;
    BOOL rc;
    char szPath=”x:\\xxxx\xxxx”;
    // 以overlapped的方式打开文件
    ghFile = CreateFile( szPath,
                    GENERIC_READ,
                    FILE_SHARE_READ|FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_FLAG_OVERLAPPED,
                    NULL
                );
    for (i=0; i<MAX_REQUESTS; i++)
    {
        //将同一文件按几个部分按overlapped方式同时读
        //注意看QueueRequest函数是如何运做的,每次读16384个块
        QueueRequest(i, i*16384, READ_SIZE);
    }
    // 等候所有操作结束;
    //隐含条件:当一个操作完成时,其对应的event对象会被激活
    WaitForMultipleObjects(MAX_REQUESTS, ghEvents, TRUE, INFINITE);
    // 收尾操作
    for (i=0; i<MAX_REQUESTS; i++)
    {
        DWORD dwNumread;
        rc = GetOverlappedResult(
                                ghFile,
                                &gOverlapped[i],
                                &dwNumread,
                                FALSE
                            );
        CloseHandle(gOverlapped[i].hEvent);
    }
    CloseHandle(ghFile);
    return EXIT_SUCCESS;
}

//当读操作完成以后,gOverlapped[nIndex].hEvent会系统被激发
int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount)
{
    //构造一个MANUAL型的event对象
    ghEvents[nIndex] = CreateEvent(NULL, TRUE, FALSE, NULL);
    //将此event对象置入OVERLAPPED结构
    gOverlapped[nIndex].hEvent = ghEvents[nIndex];
    gOverlapped[nIndex].Offset = dwLocation;
    for (i=0; i<MAX_TRY_COUNT; i++)
   {
      //文件ghFile唯一
       rc = ReadFile(ghFile, gBuffers[nIndex],&dwNumread,&gOverlapped[nIndex]);
       if (rc)
         return TRUE;
       err = GetLastError();
       if (err == ERROR_IO_PENDING)
       {
           //当错误是ERROR_IO_PENDING,那意味着读文件的操作还在进行中
          return TRUE;
       }
       // 处理一些可恢复的错误
       if ( err == ERROR_INVALID_USER_BUFFER ||
            err == ERROR_NOT_ENOUGH_QUOTA ||
            err == ERROR_NOT_ENOUGH_MEMORY )
        {
           sleep(50);
           continue;//重试
        }
        // 如果GetLastError()返回的不是以上列出的错误,放弃
        break;
    }

    return -1;

}

 

 

posted @ 2006-05-26 11:40 kenlistian 阅读(3501) | 评论 (2)编辑 收藏

转载,这篇文章非常经典,特此收录---

Email:kruglinski_at_gmail_dot_com
Blog:kruglinski.blogchina.com

早在两年前我就已经能很熟练的运用完成端口这种技术了,只是一直没有机会将它用在什么项目中,这段时间见到这种技术被过分炒作,过分的神秘化,就想写一篇解释它如何工作的文章.想告诉大家它没有传说中的那么高深难懂!有什么错误的地方还请高人指正.转载请注明出处及作者,谢谢!

以一个文件传输服务端为例,在我的机器上它只起两个线程就可以为很多个个客户端同时提供文件下载服务,程序的性能会随机器内CPU个数的增加而线性增长,我尽可能做到使它清晰易懂,虽然程序很小却用到了NT 5的一些新特性,重叠IO,完成端口以及线程池,基于这种模型的服务端程序应该是NT系统上性能最好的了.

首先.做为完成端口的基础,我们应该理解重叠IO,这需要你已经理解了内核对象及操作系统的一些概念概念,什么是信号/非信号态,什么是等待函数,什么是成功等待的副作用,什么是线程挂起等,如果这些概令还没有理解,你应该先看一下Windows 核心编程中的相关内容.如果已经理解这些,那么重叠IO对你来说并不难.

你可以这样认为重叠IO,现在你已经进入一个服务器/客户机环境, 请不要混淆概念,这里的服务器是指操作系统,而客户机是指你的程序(它进行IO操作),是当你进行IO操作(send,recv,writefile, readfile....)时你发送一个IO请求给服务器(操作系统),由服务器来完成你需要的操作,然后你什么事都没有了,当服务器完成IO请求时它会通知你,当然在这期间你可以做任何事,一个常用的技巧是在发送重叠IO请求后,程序在一个循环中一边调用PeekMessage, TranslateMessage和DispatchMessage更新界面,同时调用GetOverlappedResult等待服务器完成IO操作, 更高效一点的做法是使用IO完成例程来处理服务器(操作系统)返回的结果,但并不是每个支持重叠IO操作的函数都支持完成例程如TransmitFile 函数.

例1.一次重叠写操作过程(GetOverlappedResult方法):
1.填写一个OVERLAPPED结构
2.进行一次写操作,并指定重叠操作参数(上面的OVERLAPPED结构变量的指针)
3.做其它事(如更新界面)
4.GetOverlappedResult取操作结果
5.如果IO请求没有完成,并且没有出错则回到期3
6.处理IO操作结果

例2.一次重叠写操作过程(完成例程方法):
1.填写一个OVERLAPPED结构
2.进行一次写操作,并指定重叠操作参数(上面的OVERLAPPED结构变量的指针),并指定完成例程
3.做其它事(如更新界面)
4.当完成例程被调用说明IO操作已经完成或出错,现在可以对操作结果进行处理了


如果你已经理解上面的概念,就已经很接近IO完成端口了,当然这只是很常规的重叠操作它已经非常高效,但如果再结合多线程对一个File或是Socket进行重叠IO操作就会非常复杂,通常程序员很难把握这种复杂度.完成端口可以说就是为了充分发挥多线程和重叠IO操作相结合的性能而设计的.很多人都说它复杂,其实如果你自己实现一个多线程的对一个File或是Socket进行重叠IO操作的程序(注意是多个线程对一个HANDLE或SOCKET进行重叠 IO操作,而不是启一个线程对一个HANDLE进行重叠IO操作)就会发现完成端口实际上简化了多线程里使用重叠IO的复杂度,并且性能更高,性能高在哪?下面进行说明.

我们可能写过这样的服务端程序:

例3.主程序:
1.监听一个端口
2.等待连接
3.当有连接来时
4.启一个线程对这个客户端进行处理
5.回到2

服务线程:
1.读客户端请求
2.如果客户端不再有请求,执行6
3.处理请求
4.返回操作结果
5.回到1
6.退出线程

这是一种最简单的网络服务器模型,我们把它优化一下

例4.主程序:
1.开一个线程池,里面有机器能承受的最大线程数个线程,线程都处于挂起(suspend)状态
1.监听一个端口
2.等待连接
3.当有连接来时
4.从线程池里Resume一个线程对这个客户端进行处理
5.回到2

服务线程与例3模型里的相同,只是当线程处理完客户端所有请求后,不是退出而是回到线程池,再次挂起让出CPU时间,并等待为下一个客户机服务.当然在此期间线程会因为IO操作(服务线程的第1,5操作,也许还有其它阻塞操作)挂起自己,但不会回到线程池,也就是说它一次只能为一个客户端服务.

这可能是你能想到的最高效的服务端模型了吧!它与第一个服务端模型相比少了很多个用户态到内核态的CONTEXT Switch,反映也更加快速,也许你可能觉得这很微不足道,这说明你缺少对大规模高性能服务器程序(比如网游服务端)的认识,如果你的服务端程序要对几千万个客户端进行服务呢?这也是微软Windows NT开发组在NT 5以上的系统中添加线程池的原因.

思考一下什么样的模型可以让一个线程为多个客户端服务呢!那就要跳出每来一个连接启线程为其服务的固定思维模式,我们把线程服务的最小单元分割为单独的读或写操作(注意是读或写不是读和写),而不是一个客户端从连接到断开期间的所有读写操作.每个线程都使用重叠IO进行读写操作,投递了读写请求后线程回到线程池,等待为其它客户机服务, 当操作完成或出错时再回来处理操作结果,然后再回到线程池.

看看这样的服务器模型:
例5.主程序:
1.开一个线程池,里面有机器内CPU个数两倍的线程,线程都处于挂起(suspend)状态,它们在都等处理一次重叠IO操作的完成结果
1.监听一个端口
2.等待连接
3.当有连接来时
4.投递一个重叠读操作读取命令
5.回到2

服务线程:
1.如果读完成,则处理读取的内容(如HTTP GET命令),否则执行3
2.投递一个重叠写操作(如返回HTTP GET命令需要的网页)
3.如果是一个写操作完成,可以再投递一个重叠读操作,读取客户机的下一个请求,或者是关闭连接(如HTTP协议里每发完一个网页就断开)
4.取得下一个重叠IO操作结果,如果IO操作没有完成或没有IO操作则回到线程池

假设这是一个WEB服务器程序,可以看到工作者线程是以读或写为最小的工作单元运行的,在主程序里面进行了一次重叠读操作

当读操作完成时一个线程池中的一个工作者线程被激活取得了操作结果,处理GET或POST命令,然后发送一个网页内容,发送也是一个重叠操作,然后处理对其它客户机的IO操作结果,如果没有其它的东西需要处理时回到线程池等待.可以看到使用这种模型发送和接收可以是也可以不是一个线程.

当发送操作完成时,线程池中的一个工作者线程池激活,它关闭连接(HTTP协议),然后处理其它的IO操作结果,如果没有其它的东西需要处理时回到线程池等待.

看看在这样的模型中一个线程怎么为多个客户端服务,同样是模拟一个WEB服务器例子:

假如现在系统中有两个线程,ThreadA,ThreadB它们在都等处理一次重叠IO操作的完成结果

当一个客户机ClientA连接来时主程序投递一个重叠读操作,然后等待下一个客户机连接,当读操作完成时ThreadA被激活,它收到一个HTTP GET命令,然后ThreadA使用重叠写操作发送一个网页给ClientA,然后立即回到线程池等待处理下一个IO操作结果,这时发送操作还没有完成, 又有一个客户机ClientB连接来,主程序再投递一个重叠读操作,当读操作完成时ThreadA(当然也可能是ThreadB)再次被激活,它重复同样步骤,收到一个GET命令,使用重叠写操作发送一个网页给ClientB,这次它没有来得及回到线程池时,又有一个连接ClientC连入,主程序再投递一个重叠读操作,读操作完成时ThreadB被激活(因为ThreadA还没有回到线程池)它收到一个HTTP GET命令,然后ThreadB使用重叠写操作发送一个网页给ClientC,然后ThreadB回到线程池,这时ThreadA也回到了线程池.

可以想象现在有三个挂起的发送操作分别是ThreadA发送给ClientA和ClientB的网页,以及ThreadB发送给ClientC的网页,它们由操作系统内核来处理.ThreadA和ThreadB现在已经回到线程池,可以继续为其它任何客户端服务.

当对ClientA的重叠写操作已经完成,ThreadA(也可以是ThreadB)又被激活它关闭与ClientA连接,但还没有回到线程池,与此同时发送给ClientB的重叠写操作也完成,ThreadB被激活(因为ThreadA还没有回到线程池)它关闭与ClientB的连接,然后回到线程池,这时ClientC的写操作也完成,ThreadB再次被激活(因为ThreadA还是没有回到线程池),它再关闭与ClientC的连接,这时 ThreadA回到线程池,ThreadB也回到线程池.这时对三个客户端的服务全部完成.可以看到在整个服务过程中,"建立连接","读数据","写数据"和"关闭连接"等操作是逻辑上连续而实际上分开的.

到现在为止两个线程处理了三次读操作和三次写操作,在这些读写操作过程中所出现的状态机(state machine)是比较复杂的,我们模拟的是经过我简化过的,实际上的状态要比这个还要复杂很多,然而这样的服务端模型在客户端请求越多时与前两个模型相比的性能越高.而使用完成端口我们可以很容易实现这样的服务器模型.

微软的IIS WEB服务器就是使用这样的服务端模型,很多人说什么阿帕奇服务器比IIS的性能好什么什么的我表示怀疑,除非阿帕奇服务器可以将线程分割成,为更小的单元服务,我觉得不太可能!这种完成端口模型已经将单个读或写操作作为最小的服务单元,我觉得在相同机器配置的情况下IIS的性能要远远高于其它WEB服务器,这也是从实现机理上来分析的,如果出现性能上的差别可能是在不同的操作系统上,也许Linux的内核比Windows的要好,有人真的研究过吗?还是大家一起在炒作啊.

对于状态机概念,在很多方面都用到,TCPIP中有,编译原理中有,OpengGL中有等等,我的离散数学不好(我是会计专业不学这个),不过还是搞懂了些,我想如果你多花些时间看,还是可以搞懂的.最后是一个简单的文件传输服务器程序代码,只用了两个线程(我的机器里只有一块CPU)就可以服务多个客户端.我调试时用它同时为6个nc客户端提供文件下载服务都没有问题,当然更多也不会有问题,只是略为使用了一下NT 5的线程池和完成端口技术就可以有这样高的性能,更不用说IIS的性能咯!

希望大家不要陷在这个程序的框架中,Ctrl+C,Ctrl+V 没有什么意义,要理解它的实质.程序使用Visual C++ 6.0 SP5+2003 Platform SDK编译通过,在Windows XP Professional下调试运行通过.程序运行的最低要求是Windows 2000操作系统.

/********************************************************************
    created:    2005/12/24
    created:    24:12:2005   20:25
    modified:    2005/12/24
    filename:     d:\vcwork\iocomp\iocomp.cpp
    file path:    d:\vcwork\iocomp
    file base:    iocomp
    file ext:    cpp
    author:        kruglinski(kruglinski_at_gmail_dot_com)
   
    purpose:    利用完成端口技术实现的高性能文件下载服务程序
*********************************************************************/

#define _WIN32_WINNT    0x0500

#include <cstdlib>
#include <clocale>
#include <ctime>
#include <iostream>//一使用输入输出流程序顿时增大70K
#include <vector>
#include <algorithm>
#include <winsock2.h>
#include <mswsock.h>

using namespace std;

#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"mswsock.lib")

const int MAX_BUFFER_SIZE=1024;
const int PRE_SEND_SIZE=1024;
const int QUIT_TIME_OUT=3000;
const int PRE_DOT_TIMER=QUIT_TIME_OUT/80;

typedef enum{ IoTransFile,IoSend,IoRecv,IoQuit } IO_TYPE;

typedef struct
{
    SOCKET hSocket;
    SOCKADDR_IN ClientAddr;
}PRE_SOCKET_DATA,*PPRE_SOCKET_DATA;

typedef struct
{
    OVERLAPPED    oa;
    WSABUF        DataBuf;
    char        Buffer[MAX_BUFFER_SIZE];
    IO_TYPE        IoType;
}PRE_IO_DATA,*PPRE_IO_DATA;

typedef vector<PPRE_SOCKET_DATA>    SocketDataVector;
typedef vector<PPRE_IO_DATA>        IoDataVector;

SocketDataVector    gSockDataVec;
IoDataVector        gIoDataVec;

CRITICAL_SECTION    csProtection;

char* TimeNow(void)
{
    time_t t=time(NULL);
    tm *localtm=localtime(&t);
    static char timemsg[512]={ 0 };
   
    strftime(timemsg,512,"%Z: %B %d %X,%Y",localtm);
    return timemsg;
}

BOOL TransFile(PPRE_IO_DATA pIoData,PPRE_SOCKET_DATA pSocketData,DWORD dwNameLen)
{
    //这一句是为nc做的,你可以修改它
    pIoData->Buffer[dwNameLen-1]='\0';
   
    HANDLE hFile=CreateFile(pIoData->Buffer,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
    BOOL bRet=FALSE;

    if(hFile!=INVALID_HANDLE_VALUE)
    {
        cout<<"Transmit File "<<pIoData->Buffer<<" to client"<<endl;
        pIoData->IoType=IoTransFile;
        memset(&pIoData->oa,0,sizeof(OVERLAPPED));
        *reinterpret_cast<HANDLE*>(pIoData->Buffer)=hFile;
        TransmitFile(pSocketData->hSocket,hFile,GetFileSize(hFile,NULL),PRE_SEND_SIZE,reinterpret_cast<LPOVERLAPPED>(pIoData),NULL,TF_USE_SYSTEM_THREAD);
        bRet=WSAGetLastError()==WSA_IO_PENDING;
    }
    else
        cout<<"Transmit File "<<"Error:"<<GetLastError()<<endl;

    return bRet;
}

DWORD WINAPI ThreadProc(LPVOID IocpHandle)
{
    DWORD dwRecv=0;
    DWORD dwFlags=0;
   
    HANDLE hIocp=reinterpret_cast<HANDLE>(IocpHandle);
    DWORD dwTransCount=0;
    PPRE_IO_DATA pPreIoData=NULL;
    PPRE_SOCKET_DATA pPreHandleData=NULL;

    while(TRUE)
    {
        if(GetQueuedCompletionStatus(hIocp,&dwTransCount,
            reinterpret_cast<LPDWORD>(&pPreHandleData),
            reinterpret_cast<LPOVERLAPPED*>(&pPreIoData),INFINITE))
        {
            if(0==dwTransCount&&IoQuit!=pPreIoData->IoType)
            {
                cout<<"Client:"
                    <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
                    <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
                    <<" is closed"<<endl;

                closesocket(pPreHandleData->hSocket);

                EnterCriticalSection(&csProtection);
                    IoDataVector::iterator itrIoDelete=find(gIoDataVec.begin(),gIoDataVec.end(),pPreIoData);
                    gIoDataVec.erase(itrIoDelete);
                    SocketDataVector::iterator itrSockDelete=find(gSockDataVec.begin(),gSockDataVec.end(),pPreHandleData);
                    gSockDataVec.erase(itrSockDelete);
                LeaveCriticalSection(&csProtection);

                delete *itrIoDelete;
                delete *itrSockDelete;
               
                continue;
            }
           
            switch(pPreIoData->IoType){
            case IoTransFile:
                cout<<"Client:"
                    <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
                    <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
                    <<" Transmit finished"<<endl;
                CloseHandle(*reinterpret_cast<HANDLE*>(pPreIoData->Buffer));
                goto LRERECV;
               
            case IoSend:
                cout<<"Client:"
                    <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
                    <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
                    <<" Send finished"<<endl;

LRERECV:
                pPreIoData->IoType=IoRecv;
                pPreIoData->DataBuf.len=MAX_BUFFER_SIZE;
                memset(&pPreIoData->oa,0,sizeof(OVERLAPPED));

                WSARecv(pPreHandleData->hSocket,&pPreIoData->DataBuf,1,
                    &dwRecv,&dwFlags,
                    reinterpret_cast<LPWSAOVERLAPPED>(pPreIoData),NULL);

                break;

            case IoRecv:
                cout<<"Client:"
                    <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
                    <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
                    <<" recv finished"<<endl;
                pPreIoData->IoType=IoSend;
               
                if(!TransFile(pPreIoData,pPreHandleData,dwTransCount))
                {
                    memset(&pPreIoData->oa,0,sizeof(OVERLAPPED));
                    strcpy(pPreIoData->DataBuf.buf,"File transmit error!\r\n");
                    pPreIoData->DataBuf.len=strlen(pPreIoData->DataBuf.buf);
                   
                    WSASend(pPreHandleData->hSocket,&pPreIoData->DataBuf,1,
                        &dwRecv,dwFlags,
                        reinterpret_cast<LPWSAOVERLAPPED>(pPreIoData),NULL);
                }
                break;
               
            case IoQuit:
                goto LQUIT;
               
            default:
                ;
            }
        }   
    }
   
LQUIT:
    return 0;
}

HANDLE hIocp=NULL;
SOCKET hListen=NULL;

BOOL WINAPI ShutdownHandler(DWORD dwCtrlType)
{
    PRE_SOCKET_DATA PreSockData={ 0 };
    PRE_IO_DATA PreIoData={ 0 };

    PreIoData.IoType=IoQuit;

    if(hIocp)
    {
        PostQueuedCompletionStatus(hIocp,1,
            reinterpret_cast<ULONG_PTR>(&PreSockData),
            reinterpret_cast<LPOVERLAPPED>(&PreIoData));

        cout<<"Shutdown at "<<TimeNow()<<endl<<"wait for a moment please"<<endl;
       
        //让出CPU时间,让线程退出
        for(int t=0;t<80;t+=1)
        {
            Sleep(PRE_DOT_TIMER);
            cout<<".";
        }
       
        CloseHandle(hIocp);
    }
   
    int i=0;

    for(;i<gSockDataVec.size();i++)
    {
        PPRE_SOCKET_DATA pSockData=gSockDataVec[i];
        closesocket(pSockData->hSocket);
        delete pSockData;
    }

    for(i=0;i<gIoDataVec.size();i++)
    {
        PPRE_IO_DATA pIoData=gIoDataVec[i];
        delete pIoData;
    }

    DeleteCriticalSection(&csProtection);
    if(hListen)
        closesocket(hListen);

    WSACleanup();
    exit(0);
    return TRUE;
}

LONG WINAPI MyExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
    ShutdownHandler(0);
    return EXCEPTION_EXECUTE_HANDLER;
}

u_short DefPort=8182;

int main(int argc,char **argv)
{
    if(argc==2)
        DefPort=atoi(argv[1]);

    InitializeCriticalSection(&csProtection);
    SetUnhandledExceptionFilter(MyExceptionFilter);
    SetConsoleCtrlHandler(ShutdownHandler,TRUE);

    hIocp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

    WSADATA data={ 0 };
    WSAStartup(0x0202,&data);

    hListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(INVALID_SOCKET==hListen)
    {
        ShutdownHandler(0);
    }
   
    SOCKADDR_IN addr={ 0 };
    addr.sin_family=AF_INET;
    addr.sin_port=htons(DefPort);
   
    if(bind(hListen,reinterpret_cast<PSOCKADDR>(&addr),
        sizeof(addr))==SOCKET_ERROR)
    {
        ShutdownHandler(0);
    }
   
    if(listen(hListen,256)==SOCKET_ERROR)
        ShutdownHandler(0);

    SYSTEM_INFO si={ 0 };
    GetSystemInfo(&si);
    si.dwNumberOfProcessors<<=1;

    for(int i=0;i<si.dwNumberOfProcessors;i++)
    {
       
        QueueUserWorkItem(ThreadProc,hIocp,WT_EXECUTELONGFUNCTION);
    }
   
    cout<<"Startup at "<<TimeNow()<<endl
        <<"work on port "<<DefPort<<endl
        <<"press CTRL+C to shutdown"<<endl<<endl<<endl;

    while(TRUE)
    {
        int namelen=sizeof(addr);
        memset(&addr,0,sizeof(addr));
        SOCKET hAccept=accept(hListen,reinterpret_cast<PSOCKADDR>(&addr),&namelen);

        if(hAccept!=INVALID_SOCKET)
        {
            cout<<"accept a client:"<<inet_ntoa(addr.sin_addr)<<":"<<ntohs(addr.sin_port)<<endl;

            PPRE_SOCKET_DATA pPreHandleData=new PRE_SOCKET_DATA;
            pPreHandleData->hSocket=hAccept;
            memcpy(&pPreHandleData->ClientAddr,&addr,sizeof(addr));
           
            CreateIoCompletionPort(reinterpret_cast<HANDLE>(hAccept),
                hIocp,reinterpret_cast<DWORD>(pPreHandleData),0);
           
            PPRE_IO_DATA pPreIoData=new(nothrow) PRE_IO_DATA;

            if(pPreIoData)
            {
                EnterCriticalSection(&csProtection);
                    gSockDataVec.push_back(pPreHandleData);
                    gIoDataVec.push_back(pPreIoData);
                LeaveCriticalSection(&csProtection);

                memset(pPreIoData,0,sizeof(PRE_IO_DATA));
                pPreIoData->IoType=IoRecv;
                pPreIoData->DataBuf.len=MAX_BUFFER_SIZE;
                pPreIoData->DataBuf.buf=pPreIoData->Buffer;
                DWORD dwRecv=0;
                DWORD dwFlags=0;
                WSARecv(hAccept,&pPreIoData->DataBuf,1,
                    &dwRecv,&dwFlags,
                    reinterpret_cast<WSAOVERLAPPED*>(pPreIoData),NULL);
            }
            else
            {
                delete pPreHandleData;
                closesocket(hAccept);
            }
        }
    }
   
    return 0;
}

参考资料:
《MSDN 2001》
《Windows 网络编程》
《Windows 核心编程》
《TCP/IP详解》

--

本文章使用开源内容管理kicoy发布

posted @ 2006-05-26 09:00 kenlistian 阅读(454) | 评论 (0)编辑 收藏

I/O 完成端口可以理解为高性能、高可伸缩性的超级消息队列。若有一个事件驱动的系统,采用完成端口能很达到很高的性能。而且使用起来就会非常简单。

1。 使用 CreateIoCompletionPort API 来创建完成端口。它用于关联内核对象与完成端口的 API。在文件句柄或套接字句柄与完成端口相关联后,在该句柄上完成的所有 I/O 请求将排队
到完成端口队列中。

2。可以采用通知方式排列到完成端口队列中,使用 PostQueuedCompletionStatus API 将自定义的通知排列到完成端口队列中。
    将可以用于向线程发出信号通知事件或插入任何其他自定义外部事件。如,

HRESULT StopThreads()
{
  for (int i = 0; i < THREAD_COUNT; i++)
  {
     PostQueuedCompletionStatus(g_IOPort, 0, 0, NULL);}    
    
     //等待所有线程
     WaitForMultipleObjects(THREAD_COUNT, g_Threads, TRUE, INFINITE);
    
     for (int i = 0; i < THREAD_COUNT; i++)
     {
        CloseHandle(g_Threads[i]);
        g_Threads[i] = NULL;
     }
     return S_OK;
    }
}

而在处理GetQueuedCompletionStatus部分,则收到overlapped = null,

numberOfBytes = 0,key =0,则可以相应作出处理。比如该例中的关闭线程。

UINT __stdcall CompletionThread(PVOID param)
{
   BOOL      result      = FALSE;
   OverlappedBase* overlapped    = NULL;
   ULONG_PTR    key        = 0;
   DWORD      numberOfBytes   = 0;
   for (;;)
   {
       result = GetQueuedCompletionStatus(g_IOPort,
                 &numberOfBytes,
                 &key,
                 (OVERLAPPED**)&overlapped,
                 INFINITE);
       if (result)
       {
          if (numberOfBytes == 0 && key == 0 && !overlapped)
             break;
        ....
       }
      
   }
 
   return 0;

}

 

 

posted @ 2006-05-25 23:41 kenlistian 阅读(506) | 评论 (0)编辑 收藏

来自微软的完成端口例子,就讲解一下它的使用套路吧
反正编程这个玩意,只要用过,自然就知道什么回事,一次不会再看一次,学习这个玩意,无他,勤奋而已。
奢谈效率等等,那只是孰能生巧上的功夫。
 

  这个例子是在console下的例子,算是一个echo服务器吧,
  跑起来后将在5150端口监听,一旦有个端口连接上来,发个数据给服务端口,它就echo回数据给那个端口. 直到那个连接中断.
 
 完成端口,其实理解成一个通道或管子就可以了,和管道也差不了多少,不过可以实现异步处理罢了,
 你这边往管子里丢数据,通过GetQueuedCompletionStatus来查管子那头出数据没,出了就处理,这个管子就是通过一个自定义有点特殊的结构来写入或读出数据而已.
 那个完成端口,其实就相当是标识那个数据块的句柄,

//下面请看例子
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>

#define PORT 5150
#define DATA_BUFSIZE 8192

#pragma comment(lib, "Ws2_32")

typedef struct                        //这个玩意就是灌数据,取数据的一个自定义数据结构

                                              //和那个wm_data差不了多少,不过就是老要塞一个OverLapped结构,
{
   OVERLAPPED Overlapped;
   WSABUF DataBuf;
   CHAR Buffer[DATA_BUFSIZE];                    
   DWORD BytesSEND;                                 //发送字节数
   DWORD BytesRECV;                                
} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;


typedef struct
{
   SOCKET Socket;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;


DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);


void main(void)
{
   SOCKADDR_IN InternetAddr;
   SOCKET Listen;
   SOCKET Accept;
   HANDLE CompletionPort;
   SYSTEM_INFO SystemInfo;
   LPPER_HANDLE_DATA PerHandleData;
   LPPER_IO_OPERATION_DATA PerIoData;
   int i;
   DWORD RecvBytes;
   DWORD Flags;
   DWORD ThreadID;
   WSADATA wsaData;
   DWORD Ret;

   if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
   {
      printf("WSAStartup failed with error %d\n", Ret);
      return;
   }

   //
   //完成端口的建立得搞2次,这是第一次调用,至于为什么?我问问你
   //
   if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
   {
      printf( "CreateIoCompletionPort failed with error: %d\n", GetLastError());
      return;
   }
   //老套子api,不谈也罢
   GetSystemInfo(&SystemInfo);
  
   //发现2个CPU,那就开个双倍的线程跑吧
   for(i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
   {
      HANDLE ThreadHandle;
     
      //
      //完成端口挂到线程上面来了,就像管子把灌数据的和读数据的两头都连上了,           
     //
      if ((ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,
         0, &ThreadID)) == NULL)
      {
         printf("CreateThread() failed with error %d\n", GetLastError());
         return;
      }     
      CloseHandle(ThreadHandle);
   }

   //
   //启动一个监听socket ,以下都是长长的交代
   //
   if ((Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
      WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
   {
      printf("WSASocket() failed with error %d\n", WSAGetLastError());
      return;
   }

   InternetAddr.sin_family = AF_INET;
   InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
   InternetAddr.sin_port = htons(PORT);

   if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
   {
      printf("bind() failed with error %d\n", WSAGetLastError());
      return;
   }  

   if (listen(Listen, 5) == SOCKET_ERROR)
   {
      printf("listen() failed with error %d\n", WSAGetLastError());
      return;
   }

   //
   // 监听端口打开,就开始在这里循环,一有socket连上,WSAAccept就创建一个socket,
   // 这个socket 又和完成端口联上,
   //
   // 嘿嘿,完成端口第二次调用那个createxxx函数,为什么,留给人思考思考可能更深刻,
   // 反正这套路得来2次,
   // 完成端口completionport和accept socket挂起来了,
   //
   while(TRUE)
   {

    //主线程跑到这里就等啊等啊,但是线程却开工了,
      if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)
      {
         printf("WSAAccept() failed with error %d\n", WSAGetLastError());
         return;
      }
     
      if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA))) == NULL)
      {
         printf("GlobalAlloc() failed with error %d\n", GetLastError());
         return;
      }     
     
      PerHandleData->Socket = Accept;
     
      //
     //把这头和完成端口completionPort连起来
     //就像你把漏斗接到管子口上,开始要灌数据了
     //
      if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,
         0) == NULL)
      {
         printf("CreateIoCompletionPort failed with error %d\n", GetLastError());
         return;
      }
     
      //
      //清管子的数据结构,准备往里面灌数据
      //
      if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA))) == NULL)
      {
         printf("GlobalAlloc() failed with error %d\n", GetLastError());
         return;
      }

      ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
      PerIoData->BytesSEND = 0;
      PerIoData->BytesRECV = 0;
      PerIoData->DataBuf.len = DATA_BUFSIZE;
      PerIoData->DataBuf.buf = PerIoData->Buffer;

      Flags = 0;
     
      //
      //  accept接到了数据,就放到PerIoData中,而perIoData又通过线程中的函数取出,
     //
      if (WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
         &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
      {
         if (WSAGetLastError() != ERROR_IO_PENDING)
         {
            printf("WSARecv() failed with error %d\n", WSAGetLastError());
            return;
         }
      }
   }
}

//
//线程一但调用,就老在里面循环,
// 注意,传入的可是完成端口啊,就是靠它去取出管子中的数据
//
DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
{
   HANDLE CompletionPort = (HANDLE) CompletionPortID;
  
   DWORD BytesTransferred;
   LPOVERLAPPED Overlapped;
   LPPER_HANDLE_DATA PerHandleData;
   LPPER_IO_OPERATION_DATA PerIoData;        
   DWORD SendBytes, RecvBytes;
   DWORD Flags;
 
   while(TRUE)
   {
      //
      //在这里检查完成端口部分的数据buf区,数据来了吗?
      // 这个函数参数要看说明,
      // PerIoData 就是从管子流出来的数据,
      //PerHandleData 也是从管子里取出的,是何时塞进来的,
     //就是在建立第2次createIocompletionPort时
    // 

      if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
         (LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
      {
         printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
         return 0;
      }

      // 检查数据传送完了吗
      if (BytesTransferred == 0)
      {
         printf("Closing socket %d\n", PerHandleData->Socket);

         if (closesocket(PerHandleData->Socket) == SOCKET_ERROR)
         {
            printf("closesocket() failed with error %d\n", WSAGetLastError());
            return 0;
         }

         GlobalFree(PerHandleData);
         GlobalFree(PerIoData);
         continue;
      }    
     //
    //看看管子里面有数据来了吗?=0,那是刚收到数据
    //
      if (PerIoData->BytesRECV == 0)
      {
         PerIoData->BytesRECV = BytesTransferred;
         PerIoData->BytesSEND = 0;
      }
      else   //来了,
      {
         PerIoData->BytesSEND += BytesTransferred;
      }
  
      //
      // 数据没发完?继续send出去
     //
     if (PerIoData->BytesRECV > PerIoData->BytesSEND)
      {

         ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); //清0为发送准备
         PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND;
         PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND;

       //1个字节一个字节发送发送数据出去
         if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0,
            &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
         {
            if (WSAGetLastError() != ERROR_IO_PENDING)
            {
               printf("WSASend() failed with error %d\n", WSAGetLastError());
               return 0;
            }
         }
      }
      else
      {
         PerIoData->BytesRECV = 0;

         Flags = 0;
         ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

         PerIoData->DataBuf.len = DATA_BUFSIZE;
         PerIoData->DataBuf.buf = PerIoData->Buffer;

         if (WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
            &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
         {
            if (WSAGetLastError() != ERROR_IO_PENDING)
            {
               printf("WSARecv() failed with error %d\n", WSAGetLastError());
               return 0;
            }
         }
      }
   }
}

posted @ 2006-05-25 22:27 kenlistian 阅读(2446) | 评论 (0)编辑 收藏

这是一个简单的线程池代码,它创建一个线程队列,通过定义不同的消息,在不同的消息处理中不同的部分,这样当启动不同任务线程时,可以通过传入不同参数来处理不同的处理部分。

来处理实际的部分,

单独部分,

列出主程序代码

#include "stdafx.h"
#include "Pool.h"

int main(int argc, char* argv[])
{
    Pool thread;               //定义1个放置线程的池
    Sleep(1000);
   for (int i=0;i<10;i++)
  {
      thread.DoWork();          //运行池中线程
  }
     return 0;
}

//在建立一个线程池类,头文件

class Pool 
{
public:

    //声明为静态函数,方可在线程中建立
 static DWORD WINAPI ThreadProc(void * p);  

 int DoWork();
 Pool();
 virtual ~Pool();

private:
 static CRITICAL_SECTION sm_cs;
 std::queue<DWORD> m_qidThread;      //采用了stl中的队列存放开辟的线程

};

 

///////////////////////pool实现部分

#define MYMESSAGE (WM_USER+1000)    //自定义在线程中处理的消息
#define NUMTHREADS 10                        

CRITICAL_SECTION Pool::sm_cs;             //声明一个临界区,当在线程下操作界

                                                                   //面时,必须考虑到这是不能在线程下跑的

Pool::Pool()
{
 ::InitializeCriticalSection(&sm_cs);

 for (int i=0;i<10;i++)               //工作时立即创建10个线程,并保存在线程队列中
 {
  DWORD dw;
  HANDLE thread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, &dw);
  ::CloseHandle(thread);
  m_qidThread.push(dw);
 }
}

Pool::~Pool()
{
 while (!m_qidThread.empty())
 {
  ::PostThreadMessage(m_qidThread.front(), WM_QUIT, 0, 0);  //通知线程你要退出了
  m_qidThread.pop();
 }
 ::DeleteCriticalSection(&sm_cs);
}

DWORD WINAPI Pool::ThreadProc(void *p)
{
  MSG msg;
  while (::GetMessage(&msg, NULL, 0, 0)>0)
  {
    switch (msg.message)
    {
      case MYMESSAGE:
    //////////////////////////////////////
    // 这里加入自己的实际代码
    //
//    ::EnterCriticalSection(&sm_cs);
    std::cout << "Hello World! from Thread Number: " << ::GetCurrentThreadId() <<   std::endl;
//    ::LeaveCriticalSection(&sm_cs);

     case MYMESSAGE2:          //可以加更多的自定义消息

        //   。。。。

          break;

      default:      break;
    };
  };
  return 0;
}

//DoWork 完全可以带参数进去调用不同的消息体部分

int Pool::DoWork()
{
 DWORD dw = m_qidThread.front();
  ::PostThreadMessage(dw, MYMESSAGE, 0, 0);  //通知处理自定义消息部分的实际内容了
 m_qidThread.pop();
 m_qidThread.push(dw);
  return 0;
 
}

posted @ 2006-05-25 20:12 kenlistian 阅读(777) | 评论 (0)编辑 收藏

仅列出标题
共3页: 1 2 3