C++ thread synchronization critical section CRITICAL_SECTION

1. Critical area

A critical section, also known as a critical code segment, refers to a small piece of code that needs to monopolize some resources before the code is executed . In the program, a resource accessed by multiple threads at the same time is usually used as a critical section. It is necessary to define a variable of type CRITICAL_SECTION , and then call the InitializeCriticalSection function to initialize the variable;

Function declaration:

VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection );

lpCriticalSection: a pointer to a CRITICAL_SECTION structure representing the critical section used for initialization;

The InitializeCriticalSection function internally sets some member variables of the CRITICAL_SECTION structure, so it will not fail.

In order to define a section of code as a critical section, you need to call the EnterCriticalSection function;

VOID WINAPI EnterCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);

The function of this function is to judge whether there is a thread accessing the resource in the critical section. If not, change the value of the member variable of the CRITICAL_SECTION structure, give the current thread access right, and the function returns immediately; if a thread is accessing the resource, it will enter the waiting state until No thread access.

Release resource function:

void WINAPI LeaveCriticalSection( _Inout_LPCRITICAL_SECTION lpCriticalSection);

Free the CRITICAL_SECTION structure pointer

void WINAPI DeleteCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);

The case study

Increase a variable to 30 with three threads running simultaneously;

critical section object class

#ifndef CAUTO_LOCK_H__
#define CAUTO_LOCK_H__

class CAutoLock
{
public:
	CAutoLock();
	~CAutoLock();

	void Lock();
	void UnLock();

private:
	CRITICAL_SECTION m_Section;
};

#endif
#include "stdafx.h"
#include "CAutoLock.h"

CAutoLock::CAutoLock()
{
	InitializeCriticalSection(&m_Section);
	//Lock(); If only the lock object is defined when it is used, it is not necessary to manually enter and exit the critical area
}

CAutoLock::~CAutoLock()
{
	DeleteCriticalSection(&m_Section);
	//UnLock();
}

void CAutoLock::Lock()
{
	EnterCriticalSection(&m_Section);
}

void CAutoLock::UnLock()
{
	LeaveCriticalSection(&m_Section);
}

Three thread creation classes

#ifndef _TEST_CRITICAL_SECTION_H__
#define _TEST_CRITICAL_SECTION_H__
#include "CAutoLock.h"
class TestCriticalSection
{
public:
	TestCriticalSection();
	~TestCriticalSection();

	void StartThread();//Start thread function

	
	static DWORD __stdcall ThreadFun1(LPVOID lParam);//Thread callback function 1
	static DWORD __stdcall ThreadFun2(LPVOID lParam);//Thread callback function 2
	static DWORD __stdcall ThreadFun3(LPVOID lParam);//Thread callback function 3

private:
	HANDLE m_hThread1;
        HANDLE m_hThread2;
	HANDLE m_hThread3;

	CAutoLock m_lock;//Critical section lock common to three threads

	static int m_nTotals;
};

#endif
#include "stdafx.h"
#include "CCriticalSection.h"
#include <iostream>
using namespace std;

int TestCriticalSection::m_nTotals = 0;//Initialize static member variables

TestCriticalSection::TestCriticalSection()
{
	m_nTotals = 0;
        m_hThread1 = INVALID_HANDLE_VALUE;
	m_hThread2 = INVALID_HANDLE_VALUE;
}

TestCriticalSection::~TestCriticalSection()
{
	if (m_hThread1 != NULL)
	{
		CloseHandle(m_hThread1);
		m_hThread1 = NULL;
	}

	if (m_hThread2 != NULL)
	{
		CloseHandle(m_hThread2);
		m_hThread2 = NULL;
	}

	if (m_hThread3 != NULL)
	{
		CloseHandle(m_hThread3);
		m_hThread3 = NULL;
	}
}

DWORD __stdcall TestCriticalSection::ThreadFun1(LPVOID lParam) //static only needs to be added to the class definition, static cannot be written before the function definition outside the class definition
{
	DWORD dRet = TRUE;
	TestCriticalSection * pThis = static_cast<TestCriticalSection*>(lParam);
	while(1)
	{
		pThis->m_lock.Lock();
		pThis->m_nTotals ++;
		cout<<"ThreadFun1: m_nTotals "<<pThis->m_nTotals<<endl;
		pThis->m_lock.UnLock();
		Sleep(10);
		if (pThis->m_nTotals == 30)
		{
			break;
		}
	}

	return dRet;
}

DWORD __stdcall TestCriticalSection::ThreadFun2(LPVOID lParam)
{
	DWORD dRet = TRUE;
	TestCriticalSection * pThis = static_cast<TestCriticalSection*>(lParam);
	while(1)
	{
		pThis->m_lock.Lock();
		pThis->m_nTotals ++;
		cout<<"ThreadFun2: m_nTotals "<<pThis->m_nTotals<<endl;
		pThis->m_lock.UnLock();
		Sleep(10);
		if (pThis->m_nTotals == 30)
		{
			break;
		}
	}
	return dRet;
}

DWORD __stdcall TestCriticalSection::ThreadFun3(LPVOID lParam)
{
	DWORD dRet = TRUE;
	TestCriticalSection * pThis = static_cast<TestCriticalSection*>(lParam);
	while(1)
	{
		pThis->m_lock.Lock();
		pThis->m_nTotals ++;
		cout<<"ThreadFun3: m_nTotals "<<pThis->m_nTotals<<endl;
		pThis->m_lock.UnLock();
		Sleep(10);
		if (pThis->m_nTotals == 30)
		{
			break;
		}
	}

	return dRet;
}

void TestCriticalSection::StartThread()
{
	m_hThread1 = CreateThread(NULL, 0, &TestCriticalSection::ThreadFun1, this,  0, NULL);
	m_hThread2 = CreateThread(NULL, 0, &TestCriticalSection::ThreadFun2, this, 0, NULL);
	m_hThread3 = CreateThread(NULL, 0, &TestCriticalSection::ThreadFun3, this, 0, NULL);
}

Main function:

// CriticalSection.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "CCriticalSection.h"

int _tmain(int argc, _TCHAR* argv[])
{
	TestCriticalSection  CriticalSectionObj;
	CriticalSectionObj.StartThread();
	Sleep(5000);
	system("pause");
	return 0;
}

result:


If the code is changed to the following, no critical section is added; resource access will conflict

#include "stdafx.h"
#include "CCriticalSection.h"
#include <iostream>
using namespace std;

int TestCriticalSection::m_nTotals = 0;

TestCriticalSection::TestCriticalSection()
{
	m_nTotals = 0;
    m_hThread1 = INVALID_HANDLE_VALUE;
	m_hThread2 = INVALID_HANDLE_VALUE;
}

TestCriticalSection::~TestCriticalSection()
{
	if (m_hThread1 != NULL)
	{
		CloseHandle(m_hThread1);
		m_hThread1 = NULL;
	}

	if (m_hThread2 != NULL)
	{
		CloseHandle(m_hThread2);
		m_hThread2 = NULL;
	}

	if (m_hThread3 != NULL)
	{
		CloseHandle(m_hThread3);
		m_hThread3 = NULL;
	}
}

DWORD __stdcall TestCriticalSection::ThreadFun1(LPVOID lParam) //static only needs to be added to the class definition, static cannot be written before the function definition outside the class definition
{
	DWORD dRet = TRUE;
	TestCriticalSection * pThis = static_cast<TestCriticalSection*>(lParam);
	while(1)
	{
		//pThis->m_lock.Lock();
		pThis->m_nTotals ++;
		cout<<"ThreadFun1: m_nTotals "<<pThis->m_nTotals<<endl;
		//pThis->m_lock.UnLock();
		Sleep(10);
		if (pThis->m_nTotals == 30)
		{
			break;
		}
	}

	return dRet;
}

DWORD __stdcall TestCriticalSection::ThreadFun2(LPVOID lParam)
{
	DWORD dRet = TRUE;
	TestCriticalSection * pThis = static_cast<TestCriticalSection*>(lParam);
	while(1)
	{
		//pThis->m_lock.Lock();
		pThis->m_nTotals ++;
		cout<<"ThreadFun2: m_nTotals "<<pThis->m_nTotals<<endl;
		//pThis->m_lock.UnLock();
		Sleep(10);
		if (pThis->m_nTotals == 30)
		{
			break;
		}
	}
	return dRet;
}

DWORD __stdcall TestCriticalSection::ThreadFun3(LPVOID lParam)
{
	DWORD dRet = TRUE;
	TestCriticalSection * pThis = static_cast<TestCriticalSection*>(lParam);
	while(1)
	{
		//pThis->m_lock.Lock();
		pThis->m_nTotals ++;
		cout<<"ThreadFun3: m_nTotals "<<pThis->m_nTotals<<endl;
		//pThis->m_lock.UnLock();
		Sleep(10);
		if (pThis->m_nTotals == 30)
		{
			break;
		}
	}

	return dRet;
}

void TestCriticalSection::StartThread()
{
	m_hThread1 = CreateThread(NULL, 0, &TestCriticalSection::ThreadFun1, this,  0, NULL);
	m_hThread2 = CreateThread(NULL, 0, &TestCriticalSection::ThreadFun2, this, 0, NULL);
	m_hThread3 = CreateThread(NULL, 0, &TestCriticalSection::ThreadFun3, this, 0, NULL);
}

As a result, the following situations may occur



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325772965&siteId=291194637