C++并发及互斥保护示例

最近要写一个多线程的并发数据库,主要是希望使用读写锁实现库的并发访问,同时考虑到其他平台(如Iar)没有C++的读写锁,需要操作系统提供,就将读写锁封装起来。整个过程还是比较曲折的,碰到了不少问题,在此就简单分析总结下并发和互斥吧。

首先,先贴上一部分源代码:

cpp 复制代码
#include <shared_mutex>
#include <iostream>
#include <windows.h>
#include <synchapi.h>

using cegn_mutex = std::shared_mutex;
cegn_mutex g_cegn_mutex;
void cegn_mutex_unique_lck(cegn_mutex& testmutex)	//独占锁,写数据
{
	std::unique_lock<cegn_mutex> cegn_lock(testmutex);
}

void cegn_mutex_share_lck(cegn_mutex& Dbmutex)	//共享锁,读数据
{
	std::shared_lock<cegn_mutex> cegn_lock(Dbmutex);
}

void cegn_mutex_unlck(cegn_mutex& Dbmutex)
{
	;	//vc读写锁离开作用域自动释放
}

int g_dwVal = 0;
void FastWriteData(int i)
{
	while (1)
	{
		cegn_mutex_unique_lck(g_cegn_mutex);
		g_dwVal++;
		std::cout << "FastWriteData " << "  Set dwVal= " << g_dwVal << "\n";

		Sleep(1000);
		cegn_mutex_unlck(g_cegn_mutex);
	}
}

void SlowWriteData(int i)
{
	while (1)
	{
		cegn_mutex_unique_lck(g_cegn_mutex);
		g_dwVal++;
		std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
		Sleep(5000);
		cegn_mutex_unlck(g_cegn_mutex);
	}
}

void ReadData(int i)
{
	while (1)
	{
		cegn_mutex_share_lck(g_cegn_mutex);
		std::cout << "ReadData " << " Get dwVal= " << g_dwVal << "\n";
		Sleep(500);
		cegn_mutex_unlck(g_cegn_mutex);
	}
}

int main()
{
	std::cout << "main start !!" << std::endl;

	std::thread thread1 = std::thread(FastWriteData, 0);
	std::thread thread2 = std::thread(SlowWriteData, 0);
	thread1.join();
	thread2.join();

	getchar();
	return 1;
}

代码不长,逻辑也挺清晰的,但结果不正确:

似乎就没有互斥保护,因为FastWriteData和SlowWriteData中都独占了cegn_mutex_unique_lck(g_cegn_mutex);

且在while(1)中,不存在释放写锁的情况,那就不应该两个写线程交替出现。

如上让chatgpt分析下,它认为没啥问题,我尝试修改回标准读写锁接口,如下:

cpp 复制代码
void FastWriteData(int i)
{
	while (1)
	{
//		cegn_mutex_unique_lck(g_cegn_mutex);
		std::unique_lock<cegn_mutex> lck(g_cegn_mutex);
		g_dwVal++;
		std::cout << "FastWriteData " << "  Set dwVal= " << g_dwVal << "\n";

		Sleep(1000);
		cegn_mutex_unlck(g_cegn_mutex);
	}
}

void SlowWriteData(int i)
{
	while (1)
	{
//		cegn_mutex_unique_lck(g_cegn_mutex);
		std::unique_lock<cegn_mutex> lck(g_cegn_mutex);
		g_dwVal++;
		std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
		Sleep(5000);
		cegn_mutex_unlck(g_cegn_mutex);
	}
}

如上,代码运行就是正常了

cpp 复制代码
main start !!
FastWriteData   Set dwVal= 1
FastWriteData   Set dwVal= 2
FastWriteData   Set dwVal= 3
FastWriteData   Set dwVal= 4
FastWriteData   Set dwVal= 5
FastWriteData   Set dwVal= 6
FastWriteData   Set dwVal= 7
FastWriteData   Set dwVal= 8
FastWriteData   Set dwVal= 9
FastWriteData   Set dwVal= 10
FastWriteData   Set dwVal= 11
FastWriteData   Set dwVal= 12
FastWriteData   Set dwVal= 13
FastWriteData   Set dwVal= 14

现在FastWriteData就独占了互斥量,导致SlowWriteData无法运行。为啥使用接口:

void cegn_mutex_unique_lck(cegn_mutex& testmutex) //独占锁,写数据

{

std::unique_lock<cegn_mutex> cegn_lock(testmutex);

}

就不行了?

修改成直接调用:

cpp 复制代码
using cegn_mutex = std::shared_mutex;
cegn_mutex g_cegn_mutex;
void cegn_mutex_unique_lck(cegn_mutex& testmutex)	//独占锁,写数据
{
//	std::unique_lock<cegn_mutex> cegn_lock(testmutex);
	std::unique_lock<cegn_mutex> cegn_lock(g_cegn_mutex);
}

还是不能正确互斥,修改如下也一样:

cpp 复制代码
void cegn_mutex_unique_lck(cegn_mutex& testmutex)	//独占锁,写数据
{
//	std::unique_lock<cegn_mutex> cegn_lock(testmutex);
	std::unique_lock<std::shared_mutex> cegn_lock(g_cegn_mutex);
}

经过分析,问题是:

void cegn_mutex_unique_lck(cegn_mutex& testmutex)

函数中定义了一个互斥量cegn_lock :

std::unique_lock<cegn_mutex> cegn_lock(testmutex);

该互斥量在函数退出的时候,生命周期就结束了,所以自动销毁,最终导致无法互斥,那是在想要封装,如何实现呢,可以自己协议个类封装:

完整的简单代码如下:

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <windows.h>

class MutexWrapper {
public:
	MutexWrapper(std::mutex& mutex) : m_mutex(mutex) {
		m_mutex.lock();
	}

	~MutexWrapper() {
		m_mutex.unlock();
	}

private:
	std::mutex& m_mutex;
};

std::mutex g_mutex_test;
int g_dwVal = 0;

void FastWriteData(int i) {
	while (1) {
		MutexWrapper lock(g_mutex_test);
		g_dwVal++;
		std::cout << "FastWriteData " << "  Set dwVal= " << g_dwVal << "\n";
		Sleep(1000);
	}
}

void SlowWriteData(int i) {
	while (1) {
		MutexWrapper lock(g_mutex_test);
		g_dwVal++;
		std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
		Sleep(3000);
	}
}

int main() {
	std::cout << "main start !!" << std::endl;

	std::thread thread1 = std::thread(FastWriteData, 0);
	std::thread thread2 = std::thread(SlowWriteData, 0);
	thread1.join();
	thread2.join();

	getchar();
	return 1;
}

如此,运行正常了

修改下例程,让两个进程都整行跑

cpp 复制代码
void FastWriteData(int i) {
	while (1) {
		{
			MutexWrapper lock(g_mutex_test);
			g_dwVal++;
			std::cout << "FastWriteData " << "  Set dwVal= " << g_dwVal << "\n";
		}
		Sleep(1000);
	}
}

void SlowWriteData(int i) {
	while (1) {
		{
			MutexWrapper lock(g_mutex_test);
			g_dwVal++;
			std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
		}
		Sleep(3000);
	}
}

如上,代码就基本都正常了。

当然,也可以将互斥锁修改为读写锁,如下:

cpp 复制代码
class MutexWrapper {
public:
	MutexWrapper(std::shared_mutex& mutex) : m_mutex(mutex) {
		m_mutex.lock();
	}

	~MutexWrapper() {
		m_mutex.unlock();
	}

private:
	std::shared_mutex& m_mutex;
};

std::shared_mutex g_mutex_test;

代码也运行正常了。

综上:

1:基于RAII,C++的很多变量生命周期有限,必须特别注意智能变量的生命周期。

2:如果需要封装读写锁,不能简单函数分装,实在不行,就用一个类封装吧

3:要熟练掌握std::thread,std::shared_mutex,std::mutex的用法,这个是变法互斥基本要求

相关推荐
小九没绝活4 分钟前
设计模式-桥接模式
java·设计模式·桥接模式
JKHaaa5 分钟前
头歌 JAVA 桥接模式实验
java·开发语言·桥接模式
江沉晚呤时14 分钟前
桥接模式(Bridge Pattern)在 .NET Core 中的实现
java·开发语言·后端·c#·.netcore·net
油丶酸萝卜别吃33 分钟前
springBoot中myBatisPlus的使用
java·spring boot·后端
tt55555555555533 分钟前
嵌入式面经-C语言:智能指针,`#define` 和 `const`,`typedef`,头文件中定义静态变量
c语言·开发语言·c++
马小学编程33 分钟前
Python元组
开发语言·笔记·python·学习·职场发展
Yuze_Neko35 分钟前
C#的List和DIctionary实现原理(手搓泛型类以及增删查改等功能)
开发语言·c#·list
netyeaxi1 小时前
Java:Apache HttpClient中HttpRoute用法的介绍
java·开发语言·apache
臣妾写不来啊1 小时前
2025/03/19 Cursor使用方法(Java方向,适合Java后端把家从idea搬家到cursor)
java·ide·intellij-idea
ん贤1 小时前
优先队列 priority_queue详解
java·数据结构·c++·算法·优先队列