C++多线程2(复习向)

lock_guard

lock_guard是C++中提供的对互斥锁有关操作的高级接口,可对互斥锁进行自动上锁和解锁,多用于作为局部变量。(在对象创建时,构造函数中自动为传入的互斥锁对象上锁,局部变量被系统回收时,其析构函数自动对互斥锁对象进行解锁)
代码如下

#include 
#include 
#include
using namespace std;
//共享变量
int a = 0;
//定义信号量mutex
mutex mtx;
void add()
{
	for (int i = 0; i < 100; i++)
	{
		//等价于mtx.lock + mtx.unlock
		lock_guard<mutex> lg(mtx);
		a++;
	}
		
}

int main()
{
	thread thread1(add);
	thread thread2(add);

	thread1.join();
	thread2.join();
	cout << "共享变量a = " << a << endl;
	return 0;
}

C++多线程2(复习向)_第1张图片

unique_lock

unique_lock是比lock_guard更加强大的对互斥锁管理的接口,除了对互斥锁的自动上锁和解锁,还有延时上锁等功能,因此使用频率更高,开发中更多地使用这个接口。

void add2()
{
	for (int i = 0; i < 100; i++)
	{
	//unique_lock也可以进行自动上锁和解锁与lock_guard等效
		unique_lock<mutex> ul(mtx);
		a++;
	}
}

int main()
{
	thread thread1(add2);
	thread thread2(add2);

	thread1.join();
	thread2.join();
	cout << "共享变量a = " << a << endl;
	return 0;
}
//延迟锁,mutex不允许延迟操作
timed_mutex t_mtx;
void adde()
{
	for (int i = 0; i < 100; i++)
	{
		unique_lock<timed_mutex> ul(t_mtx, defer_lock);//加上后续的defer_lock代表不要自动加锁
		//		ul.try_lock_until() 很少使用,无法获取互斥锁就阻塞到某个时间点,过点则返回
		bool flag = ul.try_lock_for(chrono::seconds(5)); //若此时无法获取互斥锁,则只会阻塞5秒,拿到锁返回true,否则返回false
		if (flag)
			a++;
	}
}

int main()
{
	thread thread1(add2);
	thread thread2(add2);

	thread1.join();
	thread2.join();
	cout << "共享变量a = " << a << endl;
	return 0;
}

单例模式与call_once

我们在使用类时,有些类只需要创建一个对象即可满足我们的使用需求,这时我们可以使用单例模式来设计这个类
单例模式下
无参构造要放在私有域下,且需要禁用拷贝构造和重载=运算符

饿汉模式

饿汉模式,一开始就把对象创建好,即为饿汉模式

class Log {
private:
	Log(){}//单例模式下,构造函数放在private域下
	Log(const Log& l) = delete;
	Log& operator=(const Log& log) = delete;
public:
	static Log& GetInstanceHungry()
	{
		static Log log;//静态作用域下只有一个log对象,而提前创建好对象为饿汉模式
		return log;
	}
	void printLog(string s)
	{
		cout << s << endl;
	}

};

懒汉模式

需要使用时才将对象创建好为懒汉模式

//once_flag类型得放到类外,否则报错
static once_flag flag;
class Log2 {
private:
	Log2(){}
	Log2(const Log2& l) = delete;
	Log2& operator=(const Log2& log) = delete;
public:
	//需要用到再创建对象就是懒汉模式
	static Log2& GetInstanceLazy() {
		static Log2* l = nullptr;
		//call_once当多个线程都要调用获取实例的的函数时,保证只有一个线程执行call_once
		call_once(flag, [&]() {
			if (l == nullptr)
				l = new Log2;
			});
		return *l;
	}
	void printLog(string s)
	{
		cout << s << endl;
	}
};

call_once与单例模式

以懒汉模式为例,当多个线程同时打印输出时,有可能产生一种情况两者同时进入获取对象的函数,则此时静态对象会被new两次,这与我们单例模式只需要创建一个对象的设计思路就不符合了
而C++提供的call_once函数则保证了,在多个线程访问创建实例的函数时,只有一个线程去创建这个实例,因此有且只有一个实例
而饿汉不存在资源竞争关系,因为一开始就分配好了,且只有一次

#include 
#include 
#include 
#include 
using namespace std;

//单例模式,只需要创建一个对象的类,如日志类
//单例模式下又有饿汉模式和懒汉模式
class Log {
private:
	Log(){}//单例模式下,构造函数放在private域下
	Log(const Log& l) = delete;
	Log& operator=(const Log& log) = delete;
public:
	static Log& GetInstanceHungry()
	{
		static Log log;//静态作用域下只有一个log对象,而提前创建好对象为饿汉模式
		return log;
	}
	void printLog(string s)
	{
		cout << s << endl;
	}

};
//once_flag类型得放到类外,否则报错
static once_flag flag;
class Log2 {
private:
	Log2(){}
	Log2(const Log2& l) = delete;
	Log2& operator=(const Log2& log) = delete;
public:
	//需要用到再创建对象就是懒汉模式
	static Log2& GetInstanceLazy() {
		static Log2* l = nullptr;
		//call_once当多个线程都要调用获取实例的的函数时,保证只有一个线程执行call_once
		call_once(flag, [&]() {
			if (l == nullptr)
				l = new Log2;
			});
		return *l;
	}
	void printLog(string s)
	{
		cout << s << endl;
	}
};
//饿汉模式
void test01()
{
	Log::GetInstanceHungry().printLog("饿汉模式的Hello World");
}
//懒汉模式
void test02()
{
	Log2::GetInstanceLazy().printLog("懒汉模式的Hello World2");
}
//call_once 使用懒汉的Log2
void printString()
{
	Log2::GetInstanceLazy().printLog("call_once的Hello World2");
}
int main()
{
	test01();
	test02();

	thread t1(printString);
	thread t2(printString);

	t1.join();
	t2.join();

	return 0;
}

C++多线程2(复习向)_第2张图片

你可能感兴趣的:(c++,开发语言)