首页 > 代码库 > MFC线程同步初学笔记

MFC线程同步初学笔记

一、事件:

CEvent类:

  • 声明在头文件afxmt.h中
  • 根据事件对象发信后是否可以自动恢复未发信状态分为自动事件对象和手动事件对象.
  • 构造函数如下:
 1 CEvent( 2  3 BOOL bInitiallyOwn = FALSE,  //FALSE未发信,禁止系统启动运行正在等待的线程 4  5 BOOL bManualReset = FALSE,  //FALSE时自动发信 6 LPCTSTR lpszNAme = NULL, 7  8 LPSECURITY_ATTRIBUTES lpsaAttribute = NULL 9 10 );
注:
  自动发信——初始状态FALSE(未发信),需要时,可用CEvent成员函数SetEvent设置为发信状态,从而使处于等待的线程队列中第一个线程恢复运行,但事件对象随即自动会把其状态变为FALSE.故自动事件对象一次只能启动一个处于等待的线程.
  手动发信——手动事件对象,一旦用函数设置为"TRUE"发信状态,就一直处于有效状态.除非用对象的成员函数PlusEvent或ResetEvent把它重新设置为"FALSE"未发信状态。故可用于恢复多个处在等待状态的线程运行.

 举个例子——自动事件对象的应用

  程序主要功能描述:

    点击启动线程,则会启动两个线程,均弹出消息框,单机确定后,线程被自身挂起,而后单机SetEvent按钮,设置为发信状态.按顺序重新启动被线程.

  程序利用MFC对话框程序编写,主要功能代码如下:

  

/************************************************************************//* 全局事件对象                                                                     *//************************************************************************/CEvent g_eventObj;/************************************************************************//*作为子线程                                                               *//************************************************************************/UINT MsgThread1(LPVOID pParam){    LPCWSTR pMsg = (LPCWSTR)pParam;    CWnd* pMainWin = AfxGetMainWnd();    ::MessageBox(pMainWin->m_hWnd, pMsg, _T("Thread1 Message"), MB_OK);  //_T()是一个宏,    g_eventObj.Lock();        //使本线程处于等待状态    pMsg = _T("Thread1 is unlocked");    ::MessageBox(pMainWin->m_hWnd, pMsg, _T("Thread1 Message"), MB_OK);    g_eventObj.Lock();        //使本线程处于等待状态    pMsg = _T("Thread1 is unlocked again");    ::MessageBox(pMainWin->m_hWnd, pMsg, _T("Thread1 Message"), MB_OK);    return 0;}//子线程2UINT MsgThread2(LPVOID pParam){    LPCWSTR pMsg = (LPCWSTR)pParam;    CWnd* pMainWin = AfxGetMainWnd();    ::MessageBox(pMainWin->m_hWnd, pMsg, _T("Thread2 Message"), MB_OK);    g_eventObj.Lock();        //使本线程处于等待状态    pMsg = _T("Thread2 is unlocked");    ::MessageBox(pMainWin->m_hWnd, pMsg, _T("Thread2 Message"), MB_OK);    return 0;}//子线程3UINT MsgThread3(LPVOID pParam){    g_eventObj.SetEvent();        //把事件设置为发信状态    return 0;}/************************************************************************//* 启动线程 按钮消息响应                                                                   *//************************************************************************/void CThread1Dlg::OnBnClickedButtonThead(){    // TODO:  在此添加控件通知处理程序代码    AfxBeginThread( MsgThread1,_T("Thread1 is started") );    AfxBeginThread(MsgThread2, _T("Thread2 is started"));}

运行过程如下: 

  手动事件对象:  把定义事件对象的代码改为:

CEvent g_eventObj(FALSE,TRUE);

 二、临界段

  CCriticalSection类:

  • 声明在afxmt.h头文件中
  • 只能用在同一个进程中的不同线程之间
  • 用于保护独占资源,如打印机等.只允许一个线程占有某个公共资源. 
  • 它的对象叫 临界段 ,好比一把钥匙,只有获得它,线程才有权利获取资源并运行.
  • 其构造函数无参数.
  • !!千万注意: Lock()和UnLock() 成员函数必须成对使用. 一般,在线程开头使用Lock独占资源,在线程结束时使用UnLock解锁资源,以便其他线程使用.

例子程序:

/************************************************************************//* 临界段                                                                     *//************************************************************************/CCriticalSection g_criticalSection;    //不需要参数/************************************************************************//*作为子线程                                                               *//************************************************************************/UINT MsgThread(LPVOID pParam){    g_criticalSection.Lock();    LPCWSTR pMsg = (LPCWSTR)pParam;    CWnd* pMainWin = AfxGetMainWnd();    ::MessageBox(pMainWin->m_hWnd, pMsg, L"Thread1 Message", MB_OK);    g_criticalSection.Unlock();    return 0;}//子线程2UINT MsgThread2(LPVOID pParam){    g_criticalSection.Lock();    LPCWSTR pMsg = (LPCWSTR)pParam;    CWnd* pMainWin = AfxGetMainWnd();    ::MessageBox(pMainWin->m_hWnd, pMsg, L"Thread2 Message", MB_OK);    g_criticalSection.Unlock();    return 0;}/************************************************************************//* 启动线程 按钮消息响应                                                                   *//************************************************************************/void CThread1Dlg::OnBnClickedButtonThead(){    // TODO:  在此添加控件通知处理程序代码    AfxBeginThread( MsgThread,_T("Thread1 is started") );    AfxBeginThread(MsgThread2, _T("Thread2 is started"));}

  线程1先获得临界段,在关闭线程1创建的消息框后,线程2才运行。

三、互斥体

  CMutex类:

  • 可以用在不同的进程
  • 除以上一点,其余和临界段基本相同.
  • 构造函数:

CMutex(

BOOL bInitiallyOwn = FALSE, //默认互斥体状态为非锁定

LPCTSTR lpszName = NULL, //互斥体名称

LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );

四、信号量

  CSemaphore类:

  • 用于控制访问共享资源线程数目
  • 有可设初值的计数器,使用成员函数Lock时,计数器值减一;使用UnLock则加一.当计数器值为零时,禁止其他进程访问该共享资源.
  • 构造函数:
 CSemaphore(LONG lInitialCount = 1,     //计数器初值LONG lMaxCount = 1,        //计数上限LPCTSTR pstrName=NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL);

 

例子:

CSemaphore g_Smph(2,3);    //计数器初值=2,上限=3/************************************************************************//*作为子线程                                                               *//************************************************************************/UINT MsgThread(LPVOID pParam){    g_Smph.Lock();    LPCWSTR pMsg = (LPCWSTR)pParam;    CWnd* pMainWin = AfxGetMainWnd();    ::MessageBox(pMainWin->m_hWnd, pMsg, L"Thread1 Message", MB_OK);    g_Smph.Unlock();    return 0;}/************************************************************************//* 启动线程 按钮消息响应                                                                   *//************************************************************************/void CThread1Dlg::OnBnClickedButtonThead(){    // 可以以同一段代码多次创建不同线程    AfxBeginThread( MsgThread,_T("Thread1 is started") );    AfxBeginThread(MsgThread, _T("Thread2 is started"));    AfxBeginThread(MsgThread, _T("Thread3 is started"));    AfxBeginThread(MsgThread, _T("Thread4 is started"));}

运行过程: 程序先执行,进程1,2.关闭其中一个后马上执行进程3,而后执行进程4.

 

 

  附注:

  • _T()宏的作用: 
    • 问:

      #define ABC L"ABC" 

      L 宏是干什么用的,和Unicode相关吗? 
      如果这样,这和 
      #define ABC _T("ABC") 

      有分别吗?

      答:

      L表示UNICODE串,比如wchar_t* str = L"yangsongx"; 
      _T在ANSI编译模式下表示ANSI串,在UNICODE下表示UNICODE串,比如 
      TCHAR* str = _T("yangsongx"); 
      在ANSI下编译就是 char* str = "yangsongx"; 
      在UNICODE下编译就是 wchar_t* str = L"yangsongx";

          

MFC线程同步初学笔记