C++新特性31_利用C++自己封装线程同步锁(利用类的构造析构实现加锁解锁的自动化、利用引用实现在一个类中对另一个类的唯一对象的调用方法、利用对象及局部变量作用域减小颗粒度、其他类对象成员函数调用)

上两篇C++新特性29_线程同步问题的解决思路(原子操作、WindowsAPI实现原子操作、针对特定操作的WindowsAPI、如何解决同步的一般问题、 实际场景介绍解决同步问题的思路、)C++新特性30_windows api解决线程同步(临界区、手动加EnterCriticalSection(&),LeaveCriticalSection(&)、临界区大小即颗粒度、如何自动加锁)中介绍了Windows API解决同步问题的两种方法,一种是针对特定操作的API,另一种是使用临界区的形式实现,C++11对临界区的方法进行封装之后,可以在语法上避免忘记解锁等操作。

本篇介绍如何自己利用C++封装自己实现线程同步,主要用到了2个类,一个类实现基本操作的封装,一个类利用构造和析构实现加锁和解锁的自动化。

重点: 利用引用实现在一个类中对另一个类的唯一对象的调用方法如下

构造函数利用私有类中的CMyMutex& m_Mutex;作为参数,利用外部传入的对象g_mtx进行初始化,由于使用的是引用,保证了操作的对象指向了外部传入的对象g_mtx
在这里插入图片描述
引用的参考地址:C++57个入门知识点_06_ 什么是引用(需改变外部内容,用指针存在判断地址的问题、引用本质给外部内容起别名、fool(int& n) 外部函数:int& refN=n fool(refN/n))

C++新特性31_利用C++自己封装线程同步锁

1.封装临界区方法

首先新建一个CMyMutex类对临界区方法进行封装,利用类的构造和析构将对象进行构造和析构,在类中创建加锁解锁函数。

(1)CMyMutex.h:

#pragma once
#include <windows.h>

class CMyMutex
{
    
    
public:
	CMyMutex();
	~CMyMutex();
	void lock();
	void unlock();
private:
    //成员私有
	CRITICAL_SECTION m_cs;

};

CMyMutex.cpp:

#include "CMyMutex.h"

CMyMutex::CMyMutex()
{
    
    
	//使用前,需要对该对象进行初始化
	InitializeCriticalSection(&m_cs);
}

CMyMutex::~CMyMutex()
{
    
    
	//当不在使用该锁时,需要将锁删掉
	DeleteCriticalSection(&m_cs);
}

void CMyMutex::lock()
{
    
    
	//进去加锁
	EnterCriticalSection(&m_cs);
}

void CMyMutex::unlock()
{
    
    
	//出去解锁
	LeaveCriticalSection(&m_cs);
}

(2)main()中利用封装好的类进行对象创建析构及实现加锁解锁功能

#include <tchar.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <windows.h>
#include "CMyMutex.h"

using namespace std;

int g_nData = 0;
// 创建临界区对象,等价于锁
CMyMutex g_mtx;

void foo() {
    
    
	//进来上锁
	g_mtx.lock();
	for (int i = 0; i < 100000; i++) {
    
    

		g_nData++;

	}
	//出去解锁
	g_mtx.unlock();
}

int _tmain(int argc, _TCHAR* argv[])
{
    
    


	std::thread t(foo);

	//进来上锁
	g_mtx.lock();
	for (int i = 0; i < 100000; i++) {
    
    
		g_nData++;
	}
	//出去解锁
	g_mtx.unlock();

	t.join();

	std::cout << g_nData << std::endl;

	return 0;
}

运行结果:
在这里插入图片描述
上述过程实现了临界区方法的封装,但是仍然使用的是手动的加锁和解锁功能,一旦忘记添加就会造成和上篇中忘记添加解锁造成无法跳出的问题。

2.自动实现加锁和解锁

如何实现C++语法上预防出现忘记解锁的情况出现呢?
这里利用C++中对象的构造和析构实现,创建另一个类CMyLockGuard,将加锁和解锁利用构造(对象创建)与析构(出作用域)自动进行。

(1)CMyMutex.h中创建CMyLockGuard类:

#pragma once
#include <windows.h>

class CMyMutex
{
    
    
public:
	CMyMutex();
	~CMyMutex();
	void lock();
	void unlock();
private:
	CRITICAL_SECTION m_cs;

};

class CMyLockGuard 
{
    
    
public:
	//将对象的引用作为参数传递进CMyLockGuard构造中
	CMyLockGuard(CMyMutex& mtx)
		:m_Mutex(mtx)
	{
    
    
		m_Mutex.lock();
	}

	~CMyLockGuard() 
	{
    
    
		m_Mutex.unlock();
	}

private:
	//为了保证锁在使用时是唯一的,因此采用引用的形式
	CMyMutex& m_Mutex;
};

(2)CMyMutex.cpp中保持不变

(3)main中利用封装的CMyMutex类对象对CMyMutex类进行操作

其思维导图如下:利用一个类对另一个类同一个对象操作的方法-使用引用
在这里插入图片描述

#include <tchar.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <windows.h>
#include "CMyMutex.h"

using namespace std;

int g_nData = 0;
// 创建临界区对象,等价于锁
CMyMutex g_mtx;

void foo() {
    
    

	CMyLockGuard lg(g_mtx);

	for (int i = 0; i < 100000; i++) {
    
    

		g_nData++;

	}

}

int _tmain(int argc, _TCHAR* argv[])
{
    
    


	std::thread t(foo);

	CMyLockGuard lg(g_mtx);

	for (int i = 0; i < 100000; i++) {
    
    
		g_nData++;
	}


	t.join();

	std::cout << g_nData << std::endl;

	return 0;
}

(4)整个程序的思维导图
在这里插入图片描述

运行结果:
在这里插入图片描述

3.减小颗粒度

上述程序利用构造和析构实现了自动在作用域中加锁与解锁,但是整体加锁解锁的颗粒度及作用域较大,我们可以通过使用块作用域来实现减小颗粒度的目的,代码如下:

#include <tchar.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <windows.h>
#include "CMyMutex.h"

using namespace std;

int g_nData = 0;
// 创建临界区对象,等价于锁
CMyMutex g_mtx;

void foo() {
    
    

	{
    
    
		CMyLockGuard lg(g_mtx);

		for (int i = 0; i < 100000; i++) {
    
    

			g_nData++;

		}
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
    
    
	std::thread t(foo);
	
	{
    
    
		CMyLockGuard lg(g_mtx);

		for (int i = 0; i < 100000; i++) {
    
    
			g_nData++;
		}
	}

	t.join();

	std::cout << g_nData << std::endl;

	return 0;
}

对象及局部变量的作用域指的即为包含其的最小{},当运行出作用域,就会自动释放内存资源

4. 学习视频地址: 利用C++自己封装线程同步锁

猜你喜欢

转载自blog.csdn.net/Dasis/article/details/123251505