zoukankan      html  css  js  c++  java
  • 线程同步

      在多线程的情况下,如果存在多个线程要使用同一个资源的情况时,这需要在线程之间进行协调(同步)才能是程序完成预定的工作,而不会出现灾难性的冲突。为了解决多线程之间的同步问题,MFC把对线程之间进行同步的一些基本操作封装在类CSyncObject中,为了适应在各种不同的情况下同步的需要,MFC又以类CSyncObject为基类派生了四种同步类,即事件,临界段,互斥体,和信号计数器,分别为:CEvents,CCriticalSections,CMutexes,CSemphores。这些类的声明都在头文件"afxmt.h"中。

    1、事件同步类:

    CEvent( BOOL bInitiallyOwn = FALSE, //用来指定事件对象初始状态是否为发信状态

       BOOL bManualReset = FALSE, //用来指定创建事件对象是自动事件还是手工事件对象

      LPCTSTR lpszName = NULL, //用来定义事件对象的名称

      LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );//为指向一个LPSECURITY_ATTRIBUTES结构的指针

    CEvent类提供了三种对事件对象进行操控的方法:

    BOOL SetEvent( );//设置事件为发信状态,并释放其他正在等待的线程

    BOOL PulseEvent( );//设置事件为发信状态,并释放其他正在等待的线程,然后把时间设置为未发信状态

    BOOL ResetEvent( );//设置事件为未发信状态

    详细理解通过一个例子来了解工作原理:

    例子:设计一个应用程序,当用户在程序窗口上按下鼠标左键时,会创建和启动两个线程,这两个线程被启动后,各自显示一个信息框,表明线程已被启动,随即被时间对象的Lock函数把线程挂起,当用户在程序窗口按下鼠标右键时,启动另一个线程,在该线程中把时间对象设置为“发信”状态,从而启动了第一个被挂起的线程。

    实现:(1)用MFC创建一个单文档应用程序

    (2)为了使用事件对象,在应用程序的头文件中包含afxmt.h

    #include"afxmt.h"

    (3)在程序的视图类的实现文件(.cpp文件)中定义一个全局事件对象

    CEvent  eventObj;

    (4)在程序的视图类实现文件中编写如下线程函数:


    //线程函数1
    UINT MessageThread1(LPVOID pParam)
    {
     char* pMessage="Thread1 is started";//线程一对话框显示的字符串
     CWnd* pMainWnd=AfxGetMainWnd();//获取主窗口的指针,在这里主窗口的视图类的主窗口
     ::MessageBox(pMainWnd->m_hWnd,//M每一个窗口类当中有一个句柄的数据成员
      pMessage,//显示的内容
      "Thread Message",//对话框的标题
      MB_OK
      );

     eventObj.Lock();//将线程挂起,处于等待状态

     pMessage="Thread1 is unblocked";//线程一对话框显示的字符串
     //CWnd* pMainWnd=AfxGetMainWnd();//获取主窗口的指针,在这里主窗口的视图类的主窗口
     ::MessageBox(pMainWnd->m_hWnd,//M每一个窗口类当中有一个句柄的数据成员
      pMessage,//显示的内容
      "Thread Message",//对话框的标题
      MB_OK
      );

     eventObj.Lock();//将线程挂起,处于再次等待状态

     pMessage="Thread1 is unblocked again";//线程一对话框显示的字符串
     //CWnd* pMainWnd=AfxGetMainWnd();//获取主窗口的指针,在这里主窗口的视图类的主窗口
     ::MessageBox(pMainWnd->m_hWnd,//M每一个窗口类当中有一个句柄的数据成员
      pMessage,//显示的内容
      "Thread Message",//对话框的标题
      MB_OK
      );
     return 0;
    }


    //线程函数2
    UINT MessageThread2(LPVOID pParam)
    {
     char * pMessage="Thread2 is started";//线程一对话框显示的字符串
     CWnd* pMainWnd=AfxGetMainWnd();//获取主窗口的指针,在这里主窗口的视图类的主窗口
     ::MessageBox(pMainWnd->m_hWnd,//M每一个窗口类当中有一个句柄的数据成员
      pMessage,//显示的内容
      "Thread Message",//对话框的标题
      MB_OK
      );

     eventObj.Lock();//将线程挂起,处于等待状态

     pMessage="Thread2 is unblocked";//线程一对话框显示的字符串
     //CWnd* pMainWnd=AfxGetMainWnd();//获取主窗口的指针,在这里主窗口的视图类的主窗口
     ::MessageBox(pMainWnd->m_hWnd,//M每一个窗口类当中有一个句柄的数据成员
      pMessage,//显示的内容
      "Thread Message",//对话框的标题
      MB_OK
      );
     return 0;
    }

    //线程函数3
    UINT MessageThread3(LPVOID pParam)
    {
     eventObj.SetEvent();//将线程设置为事件设置为发信状态
     return 0;
    }

    (5)鼠标消息响应函数如下


    // CThreadSyncView 消息处理程序

    void CThreadSyncView::OnLButtonDown(UINT nFlags, CPoint point)
    {
     // TODO: 在此添加消息处理程序代码和/或调用默认值
     AfxBeginThread(MessageThread1,"Thread is stated");
     AfxBeginThread(MessageThread2,"Thread is stated");

     CView::OnLButtonDown(nFlags, point);
    }


    void CThreadSyncView::OnRButtonDown(UINT nFlags, CPoint point)
    {
     // TODO: 在此添加消息处理程序代码和/或调用默认值
     AfxBeginThread(MessageThread3,"Thread is unblocked");
     CView::OnRButtonDown(nFlags, point);
    }

    2、临界段同步类(CCriticalSectiond)

    类CCriticalSectiond的对象叫做临界段,他只允许一个线程占有某个共享资源,因此这个类的对象是用来保护独占式共享资源的。

    详细细节同样通过一个例子来理解:

    例子:使用临界段写一个有两个线程的应用程序

    实现:(1)用MFC创建一个单文档的应用程序

    (2)在应用程序的头文件中添加afxmt.h头文件

    #include<afxmt.h>

    (3)在视图类的实现文件中定义一个临界段的对象

    CCriticalSection criticalSection;

    (4)在视图类实现文件中定义两个线程函数


    //定义线程函数
    UINT MessageThread1(LPVOID pParam)//线程1
    {
     criticalSection.Lock();//设置临界段,此时一直执行该线程
     char * pMessage="Thread1 is started";
     CWnd * pMainWnd=AfxGetMainWnd();//这是线程函数获取视类的指针,而每一个视类的对象都包含一个该类的句柄成员
     ::MessageBox(pMainWnd->m_hWnd,//视类的句柄
      pMessage,//显示的内容
      "Thread message",//对话框的标题
      MB_OK);
     criticalSection.Unlock();//线程释放临界段
     return 0;
    }
    UINT MessageThread2(LPVOID pParam)//线程1
    {
     criticalSection.Lock();//设置临界段,此时一直执行该线程
     char * pMessage="Thread2 is started";
     CWnd * pMainWnd=AfxGetMainWnd();//这是线程函数获取视类的指针,而每一个视类的对象都包含一个该类的句柄成员
     ::MessageBox(pMainWnd->m_hWnd,//视类的句柄
      pMessage,//显示的内容
      "Thread message",//对话框的标题
      MB_OK);
     criticalSection.Unlock();//线程释放临界段
     return 0;
    }

    (5)在视图类鼠标消息响应函数中编写如下代码


    // CCriticalSectionView 消息处理程序
    void CCriticalSectionView::OnLButtonDown(UINT nFlags, CPoint point)
    {
     // TODO: 在此添加消息处理程序代码和/或调用默认值
     //单击左键就会启动两个线程
     AfxBeginThread(MessageThread1,"Thread is starting");//开启线程1
     AfxBeginThread(MessageThread2,"Thread is starting");//开启线程2

     CView::OnLButtonDown(nFlags, point);
    }

    3、互斥体类(CMutex)

    互斥体是CMutex类的对象,也只允许一个线程占有某个共享资源,以保护独占式共享资源。因此互斥体的使用方法与临界段的使用方法极为相似,所不同的是临界段只能在同一个进程中对线程进行同步,而互斥体可以在不同的进程中进行线程同步控制。

    CMutex( BOOL bInitiallyOwn = FALSE,//用来指定互斥体对象的初始状态时锁定的还是非锁定的

         LPCTSTR lpszName = NULL,//用来指定互斥体对象的名称

         LPSECURITY_ATTRIBUTES lpsaAttribute = NULL //一个指向 LPSECURITY_ATTRIBUTES结构体指针

         );

    详细过程通过一个例子来理解:

    例子:编写一个应用程序,实现进程间线程的同步

    实现:(1)用MFC创建一个单文档的应用程序

    (2)在应用程序的头文件中包含头文件afxmt.h

    #include<afxmt.h>

    (3)在视图类的实现文件中定义一个互斥体对象

    CMutex mutexObj(FALSE,"mutex1");

    (4)在视图类的实现文件中定义线程函数

    //定义线程函数
    UINT MessageThread1(LPVOID pParam)//线程函数1
    {
     //线程一旦开启,立刻用互斥体对象来锁定
     mutexObj.Lock();//锁定线程
     char* pMessage="Thread1 is started";
     //线程开启后在视类窗口区内显示一个对话框,需要获取视类句柄,而线程是在视类添加的,因此只需要获取线程的主窗口的句柄
     CWnd* pMainCwnd=AfxGetMainWnd();//获取主窗口指针,该指针包含主窗口句柄的成员
     //显示对话框
     ::MessageBox(pMainCwnd->m_hWnd,//主窗口句柄
      pMessage,//显示的内容
      "Thread message",//对话框的标题
      MB_OK//对话框有ok按钮
      );
     //完了之后就互斥体解锁,让给其他线程来运行
     mutexObj.Unlock();
     return 0;
    }

    UINT MessageThread2(LPVOID pParam)//线程函数2
    {
     //线程一旦开启,立刻用互斥体对象来锁定
     mutexObj.Lock();//锁定线程
     char* pMessage="Thread2 is started";
     //线程开启后在视类窗口区内显示一个对话框,需要获取视类句柄,而线程是在视类添加的,因此只需要获取线程的主窗口的句柄
     CWnd* pMainCwnd=AfxGetMainWnd();//获取主窗口指针,该指针包含主窗口句柄的成员
     //显示对话框
     ::MessageBox(pMainCwnd->m_hWnd,//主窗口句柄
      pMessage,//显示的内容
      "Thread message",//对话框的标题
      MB_OK//对话框有ok按钮
      );
     //完了之后就互斥体解锁,让给其他线程来运行
     mutexObj.Unlock();
     return 0;
    }

    (5)在鼠标左键按下事件响应函数中添加如下代码

    void CMutexView::OnLButtonDown(UINT nFlags, CPoint point)
    {
     // TODO: 在此添加消息处理程序代码和/或调用默认值

     //左键单击是为了启动线程
     AfxBeginThread(MessageThread1,"thread1 is starting");
     AfxBeginThread(MessageThread2,"thread2 is starting");
     CView::OnLButtonDown(nFlags, point);
    }

     4、信号量类(CSemaphore)

    信号量类是CSemaphore的对象,该对象的作用是对访问某个共享资源的线程的数目进行控制。

    信号量的构造函数:

    CSemaphore( LONG lInitialCount = 1, //用来指定设定计数器的初始值

        LONG lMaxCount = 1, //用来设定计数器的最大计数值

        LPCTSTR pstrName = NULL,

        LPSECURITY_ATTRIBUTES lpsaAttributes = NULL

          );

    详细过程通过一个例子来理解

    例子:设计一个有四个线程的应用程序,理解信号量对象的使用

    实现:(1)用MFC创建一个单文档的应用程序

    (2)在应用程序的头文件中包含afxmt.h

    #include<afxmt.h>

    (3)在视图类的实现文件中定义一个信号量对象

    CSemaphore semaphorObj(2,3);

    (4)在视图类的实现文件中定义四个线程函数


    //定义线程函数
    UINT MessageThread1(LPVOID pParam)//线程函数1
    {
     //一旦启动线程,则将该线程绑定相应的资源
     semaphoreObj.Lock();
     //用对话框的方式来提示
     char *pMessage="Thread1 is runing!!!";
     //获取线程的主窗口
     CWnd* pMainCWnd=AfxGetMainWnd();
     //弹出对话框
     ::MessageBox(
      pMainCWnd->m_hWnd,//主窗口句柄
      pMessage,//提示的内容
      "Thread message",//对话框的标题
      MB_OK
      );
     //提示完了之后就解锁
     semaphoreObj.Unlock();
     return 0;
    }
    UINT MessageThread2(LPVOID pParam)//线程函数1
    {
     //一旦启动线程,则将该线程绑定相应的资源
     semaphoreObj.Lock();
     //用对话框的方式来提示
     char *pMessage="Thread2 is runing!!!";
     //获取线程的主窗口
     CWnd* pMainCWnd=AfxGetMainWnd();
     //弹出对话框
     ::MessageBox(
      pMainCWnd->m_hWnd,//主窗口句柄
      pMessage,//提示的内容
      "Thread message",//对话框的标题
      MB_OK
      );
     //提示完了之后就解锁
     semaphoreObj.Unlock();
     return 0;
    }
    UINT MessageThread3(LPVOID pParam)//线程函数1
    {
     //一旦启动线程,则将该线程绑定相应的资源
     semaphoreObj.Lock();
     //用对话框的方式来提示
     char *pMessage="Thread3 is runing!!!";
     //获取线程的主窗口
     CWnd* pMainCWnd=AfxGetMainWnd();
     //弹出对话框
     ::MessageBox(
      pMainCWnd->m_hWnd,//主窗口句柄
      pMessage,//提示的内容
      "Thread message",//对话框的标题
      MB_OK
      );
     //提示完了之后就解锁
     semaphoreObj.Unlock();
     return 0;
    }
    UINT MessageThread4(LPVOID pParam)//线程函数1
    {
     //一旦启动线程,则将该线程绑定相应的资源
     semaphoreObj.Lock();
     //用对话框的方式来提示
     char *pMessage="Thread4 is runing!!!";
     //获取线程的主窗口
     CWnd* pMainCWnd=AfxGetMainWnd();
     //弹出对话框
     ::MessageBox(
      pMainCWnd->m_hWnd,//主窗口句柄
      pMessage,//提示的内容
      "Thread message",//对话框的标题
      MB_OK
      );
     //提示完了之后就解锁
     semaphoreObj.Unlock();
     return 0;
    }

    (5)在鼠标左键按下的消息响应函数当中添加


    // CSemaphoreView 消息处理程序

    void CSemaphoreView::OnLButtonDown(UINT nFlags, CPoint point)
    {
     // TODO: 在此添加消息处理程序代码和/或调用默认值
     //单击开启线程
     AfxBeginThread(MessageThread1,NULL);
     AfxBeginThread(MessageThread2,NULL);
     AfxBeginThread(MessageThread3,NULL);
     AfxBeginThread(MessageThread4,NULL);

     CView::OnLButtonDown(nFlags, point);
    }

  • 相关阅读:
    npm的使用
    js 数组去重
    js实现对象或者数组深拷贝
    js简单排序
    js判断类型
    鼠标移入移出事件
    jq中的attr和prop属性
    移动端底部被输入法顶起的解决办法
    vue中的number
    javascript要点(上)
  • 原文地址:https://www.cnblogs.com/ljy2013/p/3558453.html
Copyright © 2011-2022 走看看