小议避免进程退出时的死锁

来源:互联网 发布:中国食品出口数据 编辑:程序博客网 时间:2024/05/29 17:22

——Windows GUI程序中使用线程的心得体会

 ①避免在工作者线程中使用SendMessage,改用PostMessage比较好。

原因是这样的,以一个普通程序退出为例,我们经常会使用以下方式构建退出逻辑:

主线程正在消息处理函数

工作者线程正在处理任务

void OnDestroy(){//WM_DESTROY
  //1发送退出消息或命令给工作者线程:
  SetEvent(m_hStop);
  //2等待线程退出:
  WaitForSingleObject(m_hThread, INFINITE);
// DebugPoint;
  m_hThread=NULL;
  //3销毁窗口资源
  CWnd::OnDestroy();
}

void DoWork(){
  char strTitle[100]={0};
  while(WAIT_OBJECT_0 != WaitForSingleObject(m_hStop, 0)){
    Sleep(5000);//模拟工作线程的任务处理
    //访问界面资源:
    SendMessage(m_hMainWnd, WM_GETTEXT, sizeof(strTile) , strTitle);
//DebugPoint;
    Sleep(0);
  }
  CloseHandle(m_hThread);
}

如上所示,只要主线程开始处理WM_DESTROY消息后,只有等到OnDestroy返回,主线程才会处理下一个Windows消息。一旦OnDestroy函数开始执行且还没有返回,DoWork中发出的WM_GETTEXT消息就不会被主线程处理,使用SendMessage后工作者线程就会等待主线程处理WM_GETTEXT进入休眠,注意这种休眠无法使用ResumeThread唤醒(即使唤醒只要主线程没有处理WM_GETTEXT消息也会继续睡过去)。而这时OnDestroy函数正在用WaitForSingleObject等待DoWork检测m_hStop信号退出线程,所以两者互相等待都进入了休眠,这样就产生了死锁。

很多朋友一定会说,那就把OnDestroyWaitForSingleObject去掉直接往后执行不就没有死锁了么?死锁是没有了,由于OnDestroy函数的主要职责就是负责释放资源,接下来它一定会调用类似CWnd::OnDestroy()函数来销毁窗口资源,这样一来DoWork里就很有可能因为访问界面资源而崩溃。

所以,②应该避免在工作者线程中直接访问界面资源,包括不要使用GetWindowsText等函数。可以把要显示到界面上的数据,放进一个自己构建的队列中,然后使用PostMessage发出特定的消息或者用户自定义的消息,由消息处理函数去完成。

③如果必须在线程里访问界面资源怎么办?可以使用类似MFC中的界面线程。

下面是我在工作者线程里直接访问界面资源的方法,利用消息泵:

工作者线程正在处理任务

void DoWork(){
  char strTitle[100]={0};
  MSG sMsg={0};
  while(WAIT_OBJECT_0 != WaitForSingleObject(m_hStop, 0)){
    Sleep(5000);//模拟工作线程的任务处理
    //到消息队列里取出关注的消息WM_DESTROY
    if(PeekMessage(&sMsg, m_hMainWnd, WM_DESTROY, WM_DESTROY, PM_NOREMOVE)){
      break;
    }
    //尽快访问界面资源,这里还是有可能死锁的,最好不要在这中间插入指令。
    SendMessage(m_hMainWnd, WM_GETTEXT, sizeof(strTile) , strTitle);
//DebugPoint;
    Sleep(0);
  }
  //退出线程前释放相应资源:
  CloseHandle(m_hThread);
} 

 

上面也许不是解决退出死锁的万能方法,只是我的一点心得,希望大家有更好的办法也贴上来。