• 事件和进程间的数据交换 . 分类: VC++ 2013-10-09 11:47 585人阅读 评论(0) 收藏


    //========================================================================
    //TITLE:
    //    事件和进程间的数据交换
    //AUTHOR:
    //    norains
    //DATE:
    //    Monday  13-July-2009
    //Environment:
    //    WINCE5.0 + VS2005
    //========================================================================
        多线程的数据交流不难,毕竟是同属于一个进程,更为重要的是,代码还很可能同属于一个工程,基本上想怎么干就怎么干,大不了我用一个全局变量来做缓存来进行数据的交换。
       
        但对于多进程来说,就没那么容易了。从代码的角度来说,多进程意味不同的工程,意味着不能简单通过全局变量来交流数据。试想一下,如果IE有一个全局变量g_iCurPage,你用什么方法才能得到该数据?因此,在多进程的情况下,多线程那一套就没辙了。
       
        不过,如果只是交流数据,情况倒不显得那么糟糕。一般的流程无非就是:假设有两个,分别是进程A和进程B,当线程A改变某些数值时,它会通过发送相应的事件给进程B;进程B在获得该通知事件后,会采取一定的方式,读取进程A所改变的数值。

        听起来是不是很简单?
       
        在讨论这个问题之前,我们先假设这两个进程存在如下架构的代码。

    1. 进程A:  
    2. DWORD NotifyProc(LPVOID pParam)  
    3. {  
    4.  while(TRUE)  
    5.  {  
    6.   if(IsDataChange() != FALSE)  
    7.   {  
    8.    //TODO:准备好传送的数据   
    9.      
    10.    PulseEvent(hEventNotify);         
    11.   }  
    12.  }  
    13. }  
    14.   
    15. 进程B:  
    16. DWORD WaitNotifyProc(LPVOID pParam)  
    17. {  
    18.  while(TRUE)  
    19.  {  
    20.   DWORD dwReturn = WaitForSingleObject(hEventNotify);  
    21.   if(dwReturn != WAIT_TIMEOUT)  
    22.   {  
    23.    //TODO:获取相应的数据   
    24.   }  
    25.  }  
    26. }  

        从这段简单的代码之中,我们可以知道有这么两个难点,首先是进程A如何准备数据,其次便是进程B如何获取进程A准备的数据。
       
        接下来要论述的方式,都是基于这个框架的两个难点来讨论。   
       
        一般的做法,无非有三种。   
       
    1).注册表
       
        采用该方式后完善的代码如下:

    1. 进程A:  
    2. DWORD NotifyProc(LPVOID pParam)  
    3. {  
    4.  while(TRUE)  
    5.  {  
    6.   if(IsDataChange() != FALSE)  
    7.   {  
    8.    //更改相应的注册表数值   
    9.    CReg reg;  
    10.    reg.Create(HKEY_CURRENT_USER,DEVICE_INFO);  
    11.    reg.SetDW(MAIN_VOLUME,dwVal);  
    12.    reg.Close();  
    13.      
    14.    //发送通知事件   
    15.    PulseEvent(hEventNotify);         
    16.   }  
    17.  }  
    18. }  
    19.   
    20. 进程B:      
    21. DWORD WaitNotifyProc(LPVOID pParam)  
    22. {  
    23.  while(TRUE)  
    24.  {  
    25.   //等待通知事件   
    26.   DWORD dwReturn = WaitForSingleObject(hEventNotify);  
    27.     
    28.   if(dwReturn != WAIT_TIMEOUT)  
    29.   {  
    30.    //读取注册表   
    31.    CReg reg;  
    32.    reg.Create(HKEY_CURRENT_USER,DEVICE_INFO);  
    33.    DWORD dwVal = 0;  
    34.    dwVal = reg.GetDW(MAIN_VOLUME);  
    35.   }  
    36.  }  
    37. }  

        该方法灵活性非常高,进程A如果想增加更多的通知数据,只需要简单地多设注册表项。而进程B可以不用管进程A设置了多少注册表项,只需要获取自己所需要的项目即可。

        另外一个更为明显的优势在于,由于该方法是将数据保存于注册表,所以在进程B的运行是在进程A退出之后,进程B还能获取数据。甚至于,机器重启后,进程B依然能获取相应数据——前提条件是系统的注册表为Hive Registry。

        如果说缺陷,确切地说是相对于另外两种方法而言,便是速度。因为期间会对注册表进行读写,所以速度会略有损失。如果对速度非常在意,那么该方法并不是最理想的。


    2).内存印射

        其实这种方式在我blog的另一篇文章《进程间的数据共享》(http://blog.csdn.net/norains/archive/2008/07/16/2663390.aspx)有提过,但为了本篇的完整性,在这里根据我们的论述框架重新来讨论一次。
       
        原理很简单,进程A先调用CreateFileMapping开辟一个印射的内存区,然后往里面拷贝数据,最后通知进程B读取数据;进程B接受通知时,就直接调用memcpy从内存中获取数据。

    1. 进程A:  
    2. DWORD NotifyProc(LPVOID pParam)  
    3. {  
    4.  //创建内存文件印射   
    5.  HANDLE hFile = CreateFileMapping((HANDLE)-1,NULL,PAGE_READWRITE,0,MEM_SIZE,MEM_SHARE_NAME);  
    6.    
    7.  VOID * pMem = NULL;  
    8.  if(hFile != NULL)  
    9.  {  
    10.   pMem = MapViewOfFile(hFile,FILE_MAP_ALL_ACCESS,0,0,0);  
    11.  }  
    12.   
    13.  while(TRUE)  
    14.  {  
    15.   if(IsDataChange() != FALSE)  
    16.   {  
    17.    //拷贝传输的数据   
    18.    if(pMem != NULL)  
    19.    {  
    20.     memcpy(pMem,&dwValue,sizeof(dwValue));  
    21.    }  
    22.      
    23.    PulseEvent(hEventNotify);         
    24.   }  
    25.  }  
    26.    
    27.  //如果不再使用,应该关闭句柄   
    28.  CloseHandle(hFile);  
    29.   
    30. }  
    31.   
    32. 进程B:  
    33. DWORD WaitNotifyProc(LPVOID pParam)  
    34. {  
    35.  //创建内存文件印射   
    36.  HANDLE hFile = CreateFileMapping((HANDLE)-1,NULL,PAGE_READWRITE,0,MEM_SIZE,MEM_SHARE_NAME);  
    37.    
    38.  VOID * pMem = NULL;  
    39.  if(hFile != NULL)  
    40.  {  
    41.   pMem = MapViewOfFile(hFile,FILE_MAP_ALL_ACCESS,0,0,0);  
    42.  }  
    43.    
    44.  while(TRUE)  
    45.  {  
    46.   DWORD dwReturn = WaitForSingleObject(hEventNotify);  
    47.   if(dwReturn != WAIT_TIMEOUT)  
    48.   {  
    49.    //拷贝传输过来的数据   
    50.    if(pMem != NULL)  
    51.    {  
    52.     memcpy(&dwValue,pMem,sizeof(dwValue));  
    53.    }  
    54.   }  
    55.  }  
    56.    
    57.  //如果不再使用,应该关闭句柄   
    58.  CloseHandle(hFile);  
    59. }  

        该方法是最复杂的,两个进程不仅协同设置内存的大小(MEM_SIZE),还要设置同样的名称(MEM_SHARE_NAME),更要判断该内存是否能分配成功。相对的,灵活性也是最高的,只要以上问题协商解决,则什么数据类型都能传递,无论是DWORD,或是struct。当然,我们还是不能传递对象指针,因为简单地传递对象指针,则基本上都会引发内存访问违例的致命错误。

    3).设置事件数据

        相对于以上两种方式,该方式彻头彻尾只能属于轻量级。因为它方式最为简单,同样,所传递的数据也最少。
       
        其原理很简单,进程A通过SetEventData设置和事件关联的数据,然后发送事件通知进程B;进程B接收到进程A的事件以后,则通过GetEventData来获取数据。根据该原理,则代码的样式可以如下:

    1. 进程A:  
    2. DWORD NotifyProc(LPVOID pParam)  
    3. {  
    4.  while(TRUE)  
    5.  {  
    6.   if(IsDataChange() != FALSE)  
    7.   {  
    8.    //设置关联数据   
    9.    SetEventData(hEventNotify,dwData);  
    10.      
    11.    PulseEvent(hEventNotify);         
    12.   }  
    13.  }  
    14. }  
    15.   
    16. 进程B:  
    17. DWORD WaitNotifyProc(LPVOID pParam)  
    18. {  
    19.  while(TRUE)  
    20.  {  
    21.   DWORD dwReturn = WaitForSingleObject(hEventNotify);  
    22.   if(dwReturn != WAIT_TIMEOUT)  
    23.   {  
    24.    //获取关联数据   
    25.    dwData = GetEventData(hEventNotify);  
    26.   }  
    27.  }  
    28. }  

        该方式是最简单的,在传递DWORD长度类型的数据有得天独厚的优势,无论是速度还是简便性。但,也仅限于此,如果想采用该方式传递大小大于DWORD的数值,基本上会丢失精度,更不用说struct等结构体数值了。不过,这却是这三种方法之中,和事件联系最为紧密的。

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    -_-#【MongoDB】日期类型
    -_-#【Better Code】字符串匹配
    -_-#【Canvas】圆弧运动
    -_-#【Canvas】
    -_-#【AngularJS】
    COGS——C 14. [网络流24题] 搭配飞行员
    CODEVS——T 1993 草地排水 USACO
    BZOJ——T2190: [SDOI2008]仪仗队
    Codeforces_GYM_100741 A
    2017-0722 模拟赛
  • 原文地址:https://www.cnblogs.com/mao0504/p/4706754.html
Copyright © 2020-2023  润新知