首页 > 编程语言 >C++ 读写锁 shared_mutex

C++ 读写锁 shared_mutex

时间:2024-10-11 16:51:42浏览次数:10  
标签:std 线程 thread lock C++ mutex shared

C++17 新增了 std::shared_mutex, 通过shared_mutex 可以实现读写锁的功能,

 参考网址 :https://zh.cppreference.com/w/cpp/thread/shared_mutex

shared_mutex可以同时支持多个线程对共享资源同时读,但是只支持一个线程对同一共享资源进行写操作。

shared_mutex 支持共享锁和独占锁,

  • std::shared_lock<std::shared_mutex> lock(sharedMutex); //共享锁
  • std::unique_lock<std::shared_mutex> lock(sharedMutex); //独占锁

1. shared_mutex 简介

class shared_mutex;(C++17 起)

 shared_mutex类是一个同步原语,可用于保护共享数据不被多个线程同时访问。与便于独占访问的其他互斥体类型不同,shared_mutex 拥有两个访问级别:

  • 共享 - 多个线程能共享同一互斥体的所有权。
  • 独占 - 仅一个线程能占有互斥。

若一个线程已获取独占 锁(通过 locktry_lock),则无其他线程能获取该锁(包括共享的)。

若一个线程已获取共享 锁(通过 lock_sharedtry_lock_shared),则无其他线程能获取独占 锁,但可以获取共享 锁。

仅当任何线程均未获取独占 锁时,共享 锁能被多个线程获取。

在一个线程内,同一时刻只能获取一个锁(共享独占)。

共享互斥体在能由任何数量的线程同时读共享数据,但一个线程只能在无其他线程同时读写时写同一数据时特别有用

排他性锁定

lock

锁定互斥体,若互斥体不可用则阻塞
(公开成员函数)

try_lock

尝试锁定互斥体,若互斥体不可用则返回
(公开成员函数)

unlock

解锁互斥体
(公开成员函数)
共享锁定

lock_shared

为共享所有权锁定互斥体,若互斥体不可用则阻塞
(公开成员函数)

try_lock_shared

尝试为共享所有权锁定互斥体,若互斥体不可用则返回
(公开成员函数)

unlock_shared

解锁互斥体(共享所有权)
(公开成员函数)

2. 读写锁实现

2.1 通过shared_mutex 实现读写锁

#include <iostream>
#include <shared_mutex>
#include <stdio.h>
#include <thread>

class TreadSafeCounter {
private:
	mutable std::shared_mutex m_mtx;
	unsigned int m_value = 0;

public:
	TreadSafeCounter() = default;
	
	unsigned int get() const {
		std::shared_lock lock(m_mtx); // 共享锁(读锁), C++17 能自动推导出模板参数类型
		return m_value;
	}

	void increment() {
		std::unique_lock lock(m_mtx); //独占锁(写锁),C++17 能自动推导出模板参数类型
		++m_value;
	}

	void reset() {
		std::unique_lock<std::shared_mutex> lock(m_mtx); //共享锁
		m_value = 0;
	}
};

void test() {
	TreadSafeCounter counter;

	auto incrementAndPrint = [&counter](std::string threadName) {
		for (int i = 0; i < 5; ++i) {
			counter.increment();
			printf("thread name %s counter = %d \n", threadName.c_str(), counter.get());
		}
	};

	std::thread th1(incrementAndPrint, "thread_1");
	std::thread th2(incrementAndPrint, "thread_2");

	th1.join();
	th2.join();
	printf("Main thread counter = %d \n", counter.get());
}

int main() {
	test();
	return 0;
}

2.2 通过mutex和condition_variable手动实现一个读写锁

#ifndef __READWRITELOCK_H__
#define __READWRITELOCK_H__

#include <mutex>

class ReadWriteMutex {
private:
	std::mutex m_mtx;
	std::condition_variable m_cond;
	int m_readCount = 0; //当m_readCount = 0时,没有上锁; 当m_readCount > 0,读锁, 当m_readCounnt < 0,写锁

public:
	ReadWriteMutex() = default;

	void ReadLock() {
		std::unique_lock<std::mutex> lock(m_mtx);
		m_cond.wait(lock, [this]() { return m_readCount >= 0;});
		++m_readCount;
	}

	void ReadUnlock() {
		std::unique_lock<std::mutex> lock(m_mtx);
		--m_readCount;
		
		if (m_readCount == 0) {
			m_cond.notify_one(); 
		}
	}

	void WriteLock() {
		std::unique_lock<std::mutex> lock(m_mtx);
		m_cond.wait(lock, [this]() { return m_readCount == 0; });
		m_readCount = -1;
	}

	void WriteUnlock() {
		std::unique_lock<std::mutex> lock(m_mtx);
		m_readCount = 0;
		m_cond.notify_all();
	}
};


class ReadLock {
private:
	ReadWriteMutex &m_rwMutex;

public:
	ReadLock(ReadWriteMutex& readWriteMutex) : m_rwMutex(readWriteMutex) {
		m_rwMutex.ReadLock();
	}

	~ReadLock() {
		m_rwMutex.ReadUnlock();
	}

	void lock() {
		m_rwMutex.ReadLock();
	}

	void unlock() {
		m_rwMutex.ReadUnlock();
	}
};

class WriteLock {
private:
	ReadWriteMutex& m_rwMutex;

public:
	WriteLock(ReadWriteMutex& readWriteMutex) : m_rwMutex(readWriteMutex) {
		m_rwMutex.WriteLock();
	}

	~WriteLock() {
		m_rwMutex.WriteUnlock();
	}

	void Lock() {
		m_rwMutex.WriteLock();
	}

	void unlock() {
		m_rwMutex.WriteUnlock();
	}
};

#endif

测试代码

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

class ThreadSafeCounter
{
public:
    ThreadSafeCounter() = default;

    // Multiple threads/readers can read the counter's value at the same time.
    unsigned int get() const
    {
        ReadLock lock(m_mutex);
        return m_value;
    }

    // Only one thread/writer can increment/write the counter's value.
    void increment()
    {
        WriteLock lock(m_mutex);
        ++m_value;
    }

    // Only one thread/writer can reset/write the counter's value.
    void reset()
    {
        WriteLock lock(m_mutex);
        m_value = 0;
    }

private:
    mutable ReadWriteMutex m_mutex;
    unsigned int m_value = 0;
};


void test() {
    ThreadSafeCounter counter;

    auto increment_and_print = [&counter](std::string threadName)
    {
        for (int i{}; i != 6; ++i)
        {
            counter.increment();
            printf("thread name %s counter = %d \n", threadName.c_str(), counter.get());
        }
    };

    std::thread thread1(increment_and_print, "thread_1");
    std::thread thread2(increment_and_print, "thread_2");
    std::thread thread3(increment_and_print, "thread_3");

    thread1.join();
    thread2.join();
    thread3.join();
    std::cout << "main thread, count =  " << counter.get() << std::endl;
}

int main() {
    test();
	return 0;
}

标签:std,线程,thread,lock,C++,mutex,shared
From: https://blog.csdn.net/luckyhare999/article/details/142849120

相关文章

  • Dev C++ 安装与使用
    本帖子针对C/C++入门的学生。用该编译器可便于初学者入门C/C++。一、安装1、下载DevC++    百度搜索DevC++的官网点击Download等待下载2、安装点击安装这里并没有发现有支持中文的语言选项(可能是本人在语言选择的时候漏看了,见谅),选择English即可。......
  • C++删除字符串中的所有空格与换行(任意字符)
    删除字符串中的所有空格与换行使用头文件中的remove函数,注意:std::remove不会改变容器的大小,它只是将元素移动到容器的末尾。因此,我们需要调用erase来实际从字符串中删除这些元素。使用std::remove算法,它重排元素,使得要删除的元素(在这里是空格和换行符)被放在序列的末尾,......
  • C++ 算法学习——1.8 倍增与ST表
    在C++中,"倍增"(也称为"指数增长"或"指数级别增长")是一种算法优化技术,它通常用于解决一些需要频繁查询某个区间内的信息的问题,例如在处理动态规划、搜索等算法中。倍增思想的主要目的是通过预处理和存储一些中间结果,以加速后续的查询操作。具体来说,倍增思想通常包括以下步骤:......
  • C++ 算法学习——1.8 单调队列算法
    单调队列(MonotonicQueue)是一种特殊类型的队列,通常用于解决一些数组或序列相关的问题。和单调栈类似,单调队列也具有一些特定的性质,在解决一些问题时非常有用。以下是关于单调队列的一些重要点:定义:单调队列是一种数据结构,队列中的元素满足单调递增或单调递减的性质。应用:单......
  • Chromium 前端form表单提交过程分析c++
    一、本文以一个简单的HTML表单,包含两个文本输入框和一个提交按钮:<formaction="demo_form.php">Firstname:<inputtype="text"name="fname"><br>Lastname:<inputtype="text"name="lname"><br><i......
  • 实验1 现代C++编程初体验
    实验任务1代码1//现代C++标准库、算法库体验2//本例用到以下内容:3//1.字符串string,动态数组容器类vector、迭代器4//2.算法库:反转元素次序、旋转元素5//3.函数模板、const引用作为形参67#include<iostream>8#include<string>9......
  • 实验1 现代C++编程初体验
    任务1代码:1//现代C++标准库、算法库体验2//本例用到以下内容:3//1.字符串string,动态数组容器类vector、迭代器4//2.算法库:反转元素次序、旋转元素5//3.函数模板、const引用作为形参67#include<iostream>8#include<string>9#inc......
  • C#调用C++ dll教程
    C#调用C++dll教程文章目录一、创建C++dll项目二、C#程序员调用C++dll三、C++与C#数据类型对应基本数据类型对应表C++指针类型与C#类型在使用C#开发客户端时,有时需要调用C++dll,本篇博客来介绍C#程序如何调用C++dll。一、创建C++dll项目首先使用VS2022创建C++d......
  • 【C/C++内存管理】
    【知识预告】C/C++内存分布C语言中动态内存管理方式C++内存管理new和delete的实现原理常见面试题内存泄漏1C/C++内存分布intglobalVar=1;staticintstaticGlobalVar=1;voidTest(){ staticintstaticVar=1; intlocalVar=1; intnum1[10]={1,2,3,......
  • 实验1 现代C++编程初体验
    实验任务11//现代C++标准库、算法库体验2//本例用到以下内容:3//1.字符串string,动态数组容器类vector、迭代器4//2.算法库:反转元素次序、旋转元素5//3.函数模板、const引用作为形参67#include<iostream>8#include<string>9#inc......