欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

线程的互斥和同步(2)- Windows的临界区

程序员文章站 2022-07-05 10:35:35
...

临界区是指程序中的多个线程访问同一个受保护的代码段,临界区的代码段同一时刻只允许一个线程执行。

使用临界区对象 CriticalSection ,来实现对临界区的操作。
下面是一些过于临界区操作的主要函数:

  • InitializeCriticalSection (LPCRITICAL_SECTION lpCriticalSection); 初始化临界区对象
  • DeleteCriticalSection (LPCRITICAL_SECTION lpCriticalSection); 销毁临界区对象
  • EnterCriticalSection (LPCRITICAL_SECTION lpCriticalSection); 进入临界区
  • LeaveCriticalSection (LPCRITICAL_SECTION lpCriticalSection); 离开临界区
  • BOOL TryEnterCriticalSection (LPCRITICAL_SECTION lpCriticalSection); 尝试进去临界区
  1. 函数 InitializeCriticalSectionDeleteCriticalSection 是对临界区对象做初始化和销毁的工作。
  2. 函数 EnterCriticalSectionLeaveCriticalSection 中间的代码表示临界区代码,必须成对出现,否则会出现线程死锁的现象。如果有线程在临界区中,EnterCriticalSection 会阻塞,并使线程进入休眠状态,直到其他线程离开临界区并CPU调度到本线程进入临界区,该线程才会被唤起并进入临界区。
  3. 函数 TryEnterCriticalSection 尝试进入临界区,如果可以进入,则返回 TRUE ;否则返回 FALSE ,该函数不会阻塞。

下面是一个临界区使用的例子:
头文件:

#include "CThread.h"
#include "Windows.h"

extern CRITICAL_SECTION g_cs;
class CirticalThread : public CThread
{
public:
	void run(void) override;
};

这里我们同样使用类 CThread 来定义线程,run 函数为线程的入口函数
关于 CThread 的实现可以参照:
使用Windows API实现自定义线程类CThread

源文件:

#include "CirticalThread.h"
#include <iostream>

int testValue = 0;
// 临界区对象
CRITICAL_SECTION g_cs;

void CirticalThread::run(void)
{
	while (1)
	{
		// 进入临界区
		::EnterCriticalSection(&g_cs);

		std::cout << "Test Value is " << testValue++ \
				  << ", In Thread: " << ::GetCurrentThreadId() \
				  << std::endl;

		// 离开临界区
		::LeaveCriticalSection(&g_cs);

		Sleep(1000);
	}
}

这里在多个线程中同时对全局变量 testValue 做自增的操作,同时打印它的值和当前的线程ID。

调用部分

int main(int argc, char** argv)
{
	// 初始化临界区对象
	::InitializeCriticalSection(&g_cs);

	// 创建两个线程
	CirticalThread thread1;
	CirticalThread thread2;

	// 开启线程
	thread1.start();
	thread2.start();

	// 等待
	thread1.wait();
	thread2.wait();

	// 销毁临界区对象
	::DeleteCriticalSection(&g_cs);

	system("pause");
	return 0;
}

程序执行结果如下:
Test Value is 0, In Thread: 11836
Test Value is 1, In Thread: 13192
Test Value is 2, In Thread: 11836
Test Value is 3, In Thread: 13192
Test Value is 4, In Thread: 11836
Test Value is 5, In Thread: 13192


为了防止线程死锁,我们使用RALL技术。
即创造一个类对象,构造的时候进入临界区,析构的时候离开临界区。
这样临界区的进入和离开由该对象的作用域决定。

对象声明,代码如下:

class CCirticalSection
{
public:
	CCirticalSection(CRITICAL_SECTION& cs);
	~CCirticalSection();

private:
	CRITICAL_SECTION& m_cs;
};

实现如下:

CCirticalSection::CCirticalSection(CRITICAL_SECTION& cs)
	:m_cs(cs)
{
	// 进入临界区
	::EnterCriticalSection(&m_cs);
}

CCirticalSection::~CCirticalSection()
{
	// 离开临界区
	::LeaveCriticalSection(&m_cs);
}

我们修改Run函数中的内容:

void CirticalThread::run(void)
{
	while (1)
	{
		{
			CCirticalSection cs(g_cs);

			std::cout << "Test Value is " << testValue++ \
				<< ", In Thread: " << ::GetCurrentThreadId() \
				<< std::endl;
		}
		

		Sleep(1000);
	}
}

作者:douzhq
个人博客主页:不会飞的纸飞机
博客同步页(可下载完整代码):线程的互斥和同步(2)- Windows的临界区
微信公众号:不会飞的纸飞机
线程的互斥和同步(2)- Windows的临界区