C++ 各种Mutex详解

Mutex互斥锁

解释

mutex 类是能用于保护共享数据免受从多个线程同时访问的同步原语。mutex 提供排他性非递归所有权语义:
调用方线程从它成功调用 lock 或 try_lock 开始,到它调用 unlock 为止占有 mutex 。
线程占有 mutex 时,所有其他线程若试图要求 mutex 的所有权,则将阻塞(对于 lock 的调用)或收到 false 返回值(对于 try_lock ).
调用方线程在调用 lock 或 try_lock 前必须不占有 mutex 。
若 mutex 在仍为任何线程所占有时即被销毁,或在占有 mutex 时线程终止,则行为未定义。 mutex 类满足互斥体 (Mutex) 和标准布局类型 (StandardLayoutType) 的全部要求。std::mutex 既不可复制亦不可移动。

样例:

参考官方样例做了一下魔改。
开两个线程使其向全局变量vec集合放进c字符n次。

#include<iostream>
#include <mutex>
#include <thread>
#include<vector>
using namespace std;
mutex mtx;
vector<char> vec;
static void push_back_c_n_cnt(int n, char c)
{
	mtx.lock();//不加锁就注释掉
	this_thread::sleep_for(chrono::seconds(1));//卡一下时间防止太快
	cout << " push_back :" << c << endl;
	//把c推进vec 执行 n次
	for (int i = 0; i < n; i++)
	{
		cout << c << " ";
		vec.push_back(c);
	}
	puts("");
	mtx.unlock();//不加锁就注释掉
}

int main()
{
	//开启两个线程分别把*和$推进vec
	thread th1(push_back_c_n_cnt, 10, '*');
	thread th2(push_back_c_n_cnt, 10, '$');
	th1.join();
	th2.join();
	this_thread::sleep_for(chrono::seconds(1));
	cout << " print vec :" << endl;
	//打印vec
	for (int i = 0; i < vec.size(); i++)
	{
		cout << vec[i] << " ";
	}
	puts("");
	cout << "end" << endl;;
	return 0;
}

未加锁的运行结果:
在这里插入图片描述

加锁后运行结果:

在这里插入图片描述

timed_mutex 定时互斥锁

解释

timed_mutex 类是能用于保护数据免受多个线程同时访问的同步原语。以类似 mutex 的行为, timed_mutex 提供排他性非递归所有权语义。另外, timed_mutex 提供通过 try_lock_for() 和 try_lock_until() 方法试图带时限地要求 timed_mutex 所有权的能力。timed_mutex 类满足定时互斥体 (TimedMutex) 与标准布局类型 (StandardLayoutType) 的所有要求。

在规定的等待时间内,没有获取锁,线程不会一直阻塞,代码会继续执行。

try_lock_for():函数参数表示一个时间范围,在这一段时间范围之内线程如果没有获得锁则保持阻塞;如果在此期间其他线程释放了锁,则该线程可获得该互斥锁;如果超时(指定时间范围内没有获得锁),则函数调用返回false。
try_lock_until():函数参数表示一个时刻,在这一时刻之前线程如果没有获得锁则保持阻塞;如果在此时刻前其他线程释放了锁,则该线程可获得该互斥锁;如果超过指定时刻没有获得锁,则函数调用返回false。

样例:

同样是上一个功能。只是这次我们用try_lock_for()去卡住线程

#include<iostream>
#include <mutex>
#include <thread>
#include<vector>
using namespace std;
timed_mutex mtx;
vector<char> vec;
static void push_back_c_n_cnt(int n, char c)
{
	if (mtx.try_lock_for(chrono::seconds(1)))
	{
		//在1秒内获取了锁
		cout << "在1秒内获取了锁\npush_back :" << c << endl;
		//把c推进vec 执行 n次
		for (int i = 0; i < n; i++)
		{
			cout << c << " ";
			vec.push_back(c);
		}
		puts("");

		this_thread::sleep_for(chrono::seconds(1));
		//卡一下测试try_lock_for

		mtx.unlock();  //释放锁

	}
	else
	{
		//在1毫秒内没有获取锁
		cout << " 在1毫秒内没有获取锁 " << c << endl;
	}
}

int main()
{
	//开启两个线程分别把*和$推进vec
	thread th1(push_back_c_n_cnt, 10, '*');
	thread th2(push_back_c_n_cnt, 10, '$');
	th1.join();
	th2.join();
	//this_thread::sleep_for(chrono::seconds(1));
	cout << " print vec :" << endl;
	//打印vec
	for (int i = 0; i < vec.size(); i++)
	{
		cout << vec[i] << " ";
	}
	puts("");
	cout << "end" << endl;;
	return 0;
}

超时后运行结果:

在这里插入图片描述

recursive_mutex(recursive_timed_mutex同理就不说了)递归互斥锁

解释

recursive_mutex 类是同步原语,能用于保护共享数据免受从个多线程同时访问。recursive_mutex 提供排他性递归所有权语义:
调用方线程在从它成功调用 lock 或 try_lock 开始的时期里占有 recursive_mutex 。此时期间,线程可以进行对 lock 或 try_lock 的附加调用。所有权的时期在线程调用 unlock 匹配次数时结束。
线程占有 recursive_mutex 时,若其他所有线程试图要求 recursive_mutex 的所有权,则它们将阻塞(对于调用 lock )或收到 false 返回值(对于调用 try_lock )。
可锁定 recursive_mutex 次数的最大值是未指定的,但抵达该数后,对 lock 的调用将抛出 std::system_error 而对 try_lock 的调用将返回 false 。
若 recursive_mutex 在仍为某线程占有时被销毁,则程序行为未定义。 recursive_mutex 类满足互斥体 (Mutex) 和标准布局类型 (StandardLayoutType) 的所有要求。

样例

改成递归的push_back© n次

#include<iostream>
#include <mutex>
#include <thread>
#include<vector>
using namespace std;
recursive_mutex mtx;
vector<char> vec;
static void push_back_c_n_cnt(int n, char c)
{
	mtx.lock();
	if (n == 0)
	{
		mtx.unlock();
		return;
	}
	cout << n <<","<< c << endl;
	vec.push_back(c);
	push_back_c_n_cnt(n-1,c);
	mtx.unlock();

}

int main()
{
	//开启两个线程分别把*和$推进vec
	thread th1(push_back_c_n_cnt, 10, '*');
	thread th2(push_back_c_n_cnt, 10, '$');
	th1.join();
	th2.join();
	cout << " print vec :" << endl;
	//打印vec
	for (int i = 0; i < vec.size(); i++)
	{
		cout << vec[i] << " ";
	}
	puts("");
	cout << "end" << endl;;
	return 0;
}

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

如果不用recursive_mutex,会直接运行的时候则程序会死锁

shared_mutex(读写锁 C++14)

读写锁

read:
std::shared_lockstd::shared_mutex lock(_mutex);
write:
std::unique_lockstd::shared_mutex lock(_mutex);

解释

shared_mutex 类是一个同步原语,可用于保护共享数据不被多个线程同时访问。与便于独占访问的其他互斥类型不同,shared_mutex 拥有二个访问级别:
共享 - 多个线程能共享同一互斥的所有权。
独占性 - 仅一个线程能占有互斥。
若一个线程已获取独占性锁(通过 lock 、 try_lock ),则无其他线程能获取该锁(包括共享的)。
仅当任何线程均未获取独占性锁时,共享锁能被多个线程获取(通过 lock_shared 、 try_lock_shared )。
在一个线程内,同一时刻只能获取一个锁(共享或独占性)。
共享互斥体在能由任何数量的线程同时读共享数据,但一个线程只能在无其他线程同时读写时写同一数据时特别有用。
shared_mutex 类满足共享互斥体 (SharedMutex) 和标准布局类型 (StandardLayoutType) 的所有要求。

shared_mutex类是一个同步原语,可用于保护共享数据不被多个线程同时访问.与其他便于独占访问的互斥锁类型相比,shared_mutex具有两个访问级别: 共享互斥锁通常用于多个读者可以同时访问同一资源而不会导致数据争用的情况,但只有一个编写者可以这样做.

这有多种用途,但一个常见的用途是实现一个Read Write Lock,你可以让多个线程读取共享数据,但只有一个线程可以随时独占写入.因此,当您有多个读卡器时,互斥锁将以"共享模式"运行,但是当请求写入时,它将变为"独占模式".

适用场景:一个或多个读线程同时读取共享资源,且只有一个写线程来修改这个资源。

官方样例

#include <iostream>
#include <mutex>  // For std::unique_lock
#include <shared_mutex>
#include <thread>
class ThreadSafeCounter {
public:
	ThreadSafeCounter() = default;

	// Multiple threads/readers can read the counter's value at the same time.
	unsigned int get() const {
		std::shared_lock<std::shared_mutex> lock(mutex_);
		return value_;
	}

	// Only one thread/writer can increment/write the counter's value.
	void increment() {
		std::unique_lock<std::shared_mutex> lock(mutex_);
		value_++;
	}

	// Only one thread/writer can reset/write the counter's value.
	void reset() {
		std::unique_lock<std::shared_mutex> lock(mutex_);
		value_ = 0;
	}

private:
	mutable std::shared_mutex mutex_;
	unsigned int value_ = 0;
};

int main() {
	ThreadSafeCounter counter;

	auto increment_and_print = [&counter]() {
		for (int i = 0; i < 3; i++) {
			counter.increment();
			std::cout << counter.get();

			// Note: Writing to std::cout actually needs to be synchronized as well
			// by another std::mutex. This has been omitted to keep the example small.
		}
	};

	std::thread thread1(increment_and_print);
	std::thread thread2(increment_and_print);

	thread1.join();
	thread2.join();
}

多次运行结果会不同:
在这里插入图片描述
在这里插入图片描述

可以看到读的时候没被阻塞。写的时候阻塞。

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值