线程同步CriticalSection

孙鑫 第十五/十六课之四 线程同步CriticalSection

说明

在使用多线程时,一般很少有多个线程完全独立的工作。往往是多个线程同时操作一个全局变量来获取程序的运行结果。多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果是写操作,则会发生错误。这时候,我们可以通过临界区,为全局变量设置一个保护,保证同时只有一个线程可以访问此变量,其他变量进入等待状态。
      临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。

临界区,类似互斥对象Mutex,用起来比较简单,速度快,是比较推荐的一种。

一般步骤:

初始化一个临界区(新建一个电话亭,只能容纳一个人)

等待进入临界区(等待进入电话亭,进入后上锁别人继续等待,如果多次上锁则要多次开锁)

离开临界区(离开电话亭要开锁,以便让其他人进入,有几道锁开几道锁,如果不开锁则别人不能进入)

删除临界区(城管把电话亭拆了)

1 初始化

VOID InitializeCriticalSection(

LPCRITICAL_SECTION   lpCriticalSection  //[out]  CRITICAL_SECTION结构体指针,实际是struct  _RTL_CRITICAL_SECTION类型。

);

初始化一个临界区,相当于新建一个电话亭。

eg.

CRITICAL_SECTION  criticalSection;

InitializeCriticalSection(&criticalSection);

 2 进入临界区

VOID  EnterCriticalSection(

LPCRITICAL_SECTION   lpCriticalSection  // [in / out]

);

eg.

EnterCriticalSection(&criticalSection);  //进入临界区,加锁,如果多次加锁则要有多次开锁

 3 离开临界区

VOID  LeaveCriticalSection(

LPCRITICAL_SECTION   lpCriticalSection  // [in / out]

);

eg.

LeaveCriticalSection(&criticalSection); //离开临界区,开锁,有几道锁开几道锁

 4 删除临界区

VOID  DeleteCriticalSection(

LPCRITICAL_SECTION   lpCriticalSection  // [in / out]

);

删除临界区,前提是临界区必须已经用InitializeCriticalSection函数创建

eg.

DeleteCriticalSection(&criticalSection);

 5 实例

两个子线程分别给两个编辑框赋值,用临界区保证他们不同时更新

/////////////////////////////////////74CriticalSectionDlg.h  类声明///////////////////////////////

public:

    static UINT MyThread1(LPVOID lpParam);

    static UINT MyThread2(LPVOID lpParam);

 

private:

    static CRITICAL_SECTION m_criticalSection;

   /////////////////////////////////////74CriticalSectionDlg.cpp  类定义/////////////////////////////

 //初始化静态成员变量,该变量为结构体,静态成员变量作为结构体的初始化采用类似形式!

CRITICAL_SECTION CMy74CriticalSectionDlg::m_criticalSection = {0};

//对话框初始化

BOOL CMy74CriticalSectionDlg::OnInitDialog()

{

    //codes

    InitializeCriticalSection(&m_criticalSection);  //初始化临界区

    CWinThread* pThread1 = AfxBeginThread(MyThread1, (LPVOID)&m_ctrlEdit1);  //开启两个线程

    CWinThread* pThread2 = AfxBeginThread(MyThread2, (LPVOID)&m_ctrlEdit2);

    //codes

}

//子线程1

UINT CMy74CriticalSectionDlg::MyThread1(LPVOID lpParam)

{

    CString str;

    int a = 0;

    CEdit* pEdit = (CEdit*)lpParam;

 

    while (true)

    {

        EnterCriticalSection(&m_criticalSection); //等待进入临界区,进入后加锁使其他线程不能进入

        str.Format("%d", ++a);

        str += "  a";

        pEdit->SetWindowText(str);

        Sleep(100);

        if (a >= 1000)

        {

            LeaveCriticalSection(&m_criticalSection);  //当a>=1000时线程1退出,如果不加此句则线程2永远不能进入临界区

            break;

        }

        LeaveCriticalSection(&m_criticalSection);  //离开 开锁

    }

    return 0;

}

 //子线程2

UINT CMy74CriticalSectionDlg::MyThread2(LPVOID lpParam)

{

    CString str;

    int b = 0;

    CEdit* pEdit = (CEdit*)lpParam;

 

    while (true)

    {

        EnterCriticalSection(&m_criticalSection);//等待进入临界区,进入后加锁

        str.Format("%d", ++b);

        str += "  b";

        pEdit->SetWindowText(str);

        Sleep(100);

        LeaveCriticalSection(&m_criticalSection); //离开 开锁

    }

    return 0;

}

NOTE:如果子线程1或2中调用了多次EnterCriticalSection,在线程退出或一次循环结束时也要调用多次LeaveCriticalSection。

原文地址:https://www.cnblogs.com/lingc/p/3380091.html