C++多线程编程学习笔记

本文详细记录了C++多线程编程的学习笔记,包括HelloWorld示例、线程分离、数据共享与互斥、死锁防范、智能指针、条件变量、原子操作、异步编程(future/packaged_task)以及单例模式的应用。
摘要由CSDN通过智能技术生成

C++多线程编程学习笔记


推荐的C++多线程编程学习链接: C++11 多线程编程-小白零基础到手撕线程池.

自己按照课程敲的代码段

#include <iostream>
#include <thread>
#include <string>
#include <memory> // 智能指针使用
#include <mutex> // 互斥锁使用
#include <windows.h> // Sleep使用
#include <string>
#include <queue>
#include <condition_variable> 
#include <atomic>
#include <future> //future packaged_task async

// 第一节课程
void PrintHelloWord(std::string str)
{
	std::cout << str << std::endl;
}
void DetachFunction(std::string str)
{
	std::cout << str << std::endl;
}
void PrintBigData()
{
	for (long long i = 0; i < 1e1; ++i) {
		std::cout << i << std::endl;
	}
}

// 第二节课程
void ReferenceFunc(int& RefF)
{
	RefF++;
	std::cout << "ReferenceFunc RefF value:" << RefF << std::endl;
}
void PtrFunc(int * p) {
	std::cout << "*p" << *p << std::endl;
}
class A {
public:
	void foo() {
	std::cout << "A class execute!" << std::endl;
	}

private:
	friend void friendFunc(A *p);
	void privateFunc() {
		std::cout << "A class privateFunc execute!" << std::endl;
	}
};

void friendFunc(A* p) {
	p->privateFunc();
}

std::mutex mtx;
// 第三节课程 互斥量解决数据共享问题
void AddFuncForMutex(int& a_mtx) {
	for (long long i = 0; i < 1e6; i++) {
		mtx.lock(); // 不加锁数据不对,枷锁就对了
		a_mtx++;
		mtx.unlock();
		// 上面的上锁和解锁可以改用std::lock_guard<std::mutex> guard(mtx); // 不用管所锁的lock和unlock
	}
}

// 死锁:2把锁互相等着对方释放
std::mutex mtx1;
std::mutex mtx2;
void func_1_lock()
{
	mtx1.lock();
	Sleep(3000);
	std::cout << "1" << std::endl;
	mtx2.lock();
	std::cout << "2" << std::endl;
	mtx1.unlock();
	std::cout << "3" << std::endl;
	mtx2.unlock();
	std::cout << "4" << std::endl;
}
void func_2_lock()
{
	mtx2.lock();
	std::cout << "5" << std::endl;
	mtx1.lock();
	std::cout << "6" << std::endl;
	mtx2.unlock();
	std::cout << "7" << std::endl;
	mtx1.unlock();
	std::cout << "8" << std::endl;
}

void AddFuncForMutexLockGuard(int& a_mtx) {
	for (long long i = 0; i < 1e6; i++) {
		std::lock_guard<std::mutex> guard(mtx); // 不用管锁的lock和unlock,构造析构解决
		a_mtx++;
	}
}

void AddFuncForMutexUniqueGuard(int& a_mtx) {
	for (long long i = 0; i < 1e6; i++) {
		std::unique_lock<std::mutex> guard(mtx); // 不用管锁的lock和unlock
		a_mtx++;
	}
}

std::timed_mutex t_mtx;
void AddFuncForMutexUniqueGuardTime(int& a_mtx) {
	for (long long i = 0; i < 2; i++) {
		std::unique_lock<std::timed_mutex> guard(t_mtx, std::defer_lock); // 构造时不上锁
		if (guard.try_lock_for(std::chrono::seconds(2))) { // 等待2秒看能否上锁
			std::this_thread::sleep_for(std::chrono::seconds(1));
			a_mtx++;
		}
	}
}

// 饿汉模式
static std::once_flag once;
class Log 
{
private:
	//Log() = default;
	static Log* log;
public:
	static Log* GetInstance() {
		//static Log log1; // 饿汉模式
		//return log1;
		std::call_once(once, init); // 防止多线程调用多次
		return log;
	}
	void PrintErrLog()
	{
		std::cout << __TIME__ <<"SingleTon there is error!"<< std::endl;
	}
	static void init() {
		if (log == nullptr) // 懒汉模式线程不安全
		{
			log = new Log();
			std::cout << "Log GetInstance init only execute once" << std::endl;
		}
	}
};
Log* Log::log = nullptr; // 类中静态成员需要初始化
void PrintSingleTonErrorLog()
{
	Log::GetInstance()->PrintErrLog();
}

// 生产者消费者
std::queue<int> g_queue;
std::condition_variable g_cv;
bool stop = false;
void Productor() {
	for (int i = 0; i < 5; i++) {
		{
			std::unique_lock<std::mutex> lock(mtx);
			g_queue.push(i);
			g_cv.notify_one();// 知会消费者一次
			std::cout << "Productor : " <<  i <<std::endl;
		}
		std::this_thread::sleep_for(std::chrono::microseconds(100)); // 延迟100毫秒让显示全
 	}
	stop = true;
	g_cv.notify_one();// 最后知会消费者一次,把程序退出

}
void Consumer() {
	while (1) {
		std::unique_lock<std::mutex> lock(mtx);
		g_cv.wait(lock, []() {return !g_queue.empty() || stop; }); // 由notify_one知会是否阻塞这里
		if (stop) {
			break;
		}
		int value = g_queue.front();
		g_queue.pop();
		std::cout << "Consumer : " << value << std::endl;
	}
}

// 原子变量
std::atomic<int> data(0);
void AtomicFunc() {
	for (int i = 0; i < 1000; i++) {
		data++; // 原子变量自带锁,且效率更高
	}
}

// async future packaged_task promise
int AsyncFunc() {
	int i;
	for (i = 0; i < 1000; i++) {
		data++; // 原子变量自带锁,且效率更高
	}
	return i;
}

void PromiseFunc(std::promise<int> &f) {
	f.set_value(100);
}




int main()
{
	std::cout<<"course one start"<<std::endl;
	std::thread thread1(PrintHelloWord, "hello hello");
	thread1.join(); // 这条语句保证只有当子线程thread1运行完毕,主线程才会接着往下
	// 当一个线程调用了join函数时,它会被阻塞,直到被调用join函数的线程执行结束
	// 主线程回收子线程资源

	std::thread thread2(DetachFunction, "DetachFunction");
	thread2.detach(); // 主线程可以先于子线程结束,主线程和子线程分离,子线程的资源由其他线程释放

	std::thread thread3(PrintBigData);
	if (thread3.joinable()) { // 确定可以用join时再用
		thread3.join();
	}
	std::cout << "over!" << std::endl; //只有当thread3中的数打印完以后,主线程才往下执行
	std::cout << "course one end" << std::endl;

	std::cout << "course two start" << std::endl;
	int RefF = 1;
	std::thread thread4(ReferenceFunc, std::ref(RefF)); // 引用变量传递的使用,得用ref()函数
	thread4.join();
	std::cout <<  "RefF: " << RefF << std::endl;
	
	int* ptr = new int(1);
	std::thread thread5(PtrFunc, ptr);
	delete ptr; //因为主线陈已经主动释放指针了,所以子线程thread5中打印为随机值,不为1
	thread5.join();

	// 线程在调用类对象时,最好用智能指针,解决智能指针的释放问题
	std::shared_ptr<A> a = std::make_shared<A>(); // 这里只能用shared,不能用ubique
	//std::unique_ptr<A> b = std::make_unique<A>();
	std::thread thread6(&A::foo, a); // 第一个参数传的是类函数的地址,第二个为类对象的地址,即指针
	A b;
	std::thread thread7(friendFunc, &b); // 第一个参数传的是友元函数,第二个为类对象的地址,即指针
	std::cout << "course two end" << std::endl;

	std::cout << "course three start 互斥量解决数据共享问题" << std::endl;
	// 1.避免数据竞争
	int a_mtx = 0;
	std::thread thread8(AddFuncForMutex, std::ref(a_mtx));
	std::thread thread9(AddFuncForMutex, std::ref(a_mtx));
	thread8.join(); // 这里不把thread8.join()写在定义thread8后面,就是想让thread8和thread9数据竞争
	thread9.join();
	std::cout << "数据竞争:a_mtx value shoud be 2e6? actual a_mtx:" << a_mtx << std::endl;
	// 2.死锁
	//std::thread thread10(func_1_lock);
	//std::thread thread11(func_2_lock); // 打印只有5/n1,死锁了
	 死锁的解决方法:对于每个子线程,都先获取mtx1锁,再获取mtx2锁
	//thread10.join();
	//thread11.join();
	std::cout << "死锁结束" << std::endl;
	// 3.std::lock_guard 互斥量封装类,用于保护共享数据,防止数据竞争,只能在局部作用域中使用
	a_mtx = 0;
	std::thread thread12(AddFuncForMutexLockGuard, std::ref(a_mtx));
	std::thread thread13(AddFuncForMutexLockGuard, std::ref(a_mtx));
	thread12.join();
	thread13.join();
	std::cout << "lock_guard:a_mtx value shoud be 2e6? actual a_mtx:" << a_mtx << std::endl;
	// 4.C++11 std::unique_lock 更加灵活的处理,延迟枷锁、条件变量、超时,同时占的资源也会增多
	// 4.1 有lock_guard功能
	a_mtx = 0;
	std::thread thread14(AddFuncForMutexUniqueGuard, std::ref(a_mtx));
	std::thread thread15(AddFuncForMutexUniqueGuard, std::ref(a_mtx));
	thread14.join();
	thread15.join();
	std::cout << "unique_lock:a_mtx value shoud be 2e6? actual a_mtx:" << a_mtx << std::endl;
	// 4.2 等待一段时间再上锁,如果时间超时直接返回
	a_mtx = 0;
	std::thread thread16(AddFuncForMutexUniqueGuardTime, std::ref(a_mtx));
	std::thread thread17(AddFuncForMutexUniqueGuardTime, std::ref(a_mtx));
	thread16.join();
	thread17.join();
	std::cout << "unique_lock:a_mtx value shoud be 3? actual a_mtx:" << a_mtx << std::endl; // 卡伊画时序图看看为啥是3,有一次冲掉了
	// 5.单例,防止new多份,除了call_once,在GetInstance();里面用lock_guard<mutex> lock(mtx);也是可以解决的
	std::thread thread18(PrintSingleTonErrorLog);
	std::thread thread19(PrintSingleTonErrorLog);
	thread18.join();
	thread19.join();
	// 6.condition_variable使用场景 生产者-消费者模型
	std::thread thread20(Productor);
	std::thread thread21(Consumer);
	thread20.join();
	thread21.join();
	// 7.原子变量自带锁且效率更高
	std::thread thread22(AtomicFunc);
	std::thread thread23(AtomicFunc);
	thread22.join();
	thread23.join();
	std::cout << "data:" << data << std::endl;
	data.store(1); // 给data赋值1,保证操作是原子的,线程安全的
	std::cout << "data:" << data.load() << std::endl; //.load() 也可以输出
	//8. 异步
	//8.1
	std::future<int> future_reslt = std::async(std::launch::async, AsyncFunc); // 这句表示AsyncFunc已经在后台执行了,已经是另外一个线程了
	std::cout << "AsyncFunc: " << AsyncFunc() << std::endl; // AsyncFunc函数里面没有共享的数据,所以没有数据竞争,不用考虑锁
	std::cout << "future_reslt.get(): " << future_reslt.get() << std::endl;
	//8.2
	std::packaged_task<int()> task(AsyncFunc); // 封装一下
	auto future_reslt1 = task.get_future();
	std::thread thread24(std::move(task)); // packaged_task是一个可移动对象,放到Thread中执行需要转义
	thread24.join();
	std::cout << "AsyncFunc: " << AsyncFunc() << std::endl; // AsyncFunc函数里面没有共享的数据,所以没有数据竞争,不用考虑锁
	std::cout << "future_reslt1.get(): " << future_reslt1.get() << std::endl; // get()会阻塞当前线程,直到获取
	//8.3 promise 用于在一个线程中产生值,另外一个线程去使用,与future搭配使用
	std::promise<int> f;
	auto future_result2 = f.get_future();
	std::thread thread25(PromiseFunc, std::ref(f));
	thread25.join();
	std::cout << "future_result2.get(): " << future_result2.get() << std::endl;
	std::cout << "course three end 互斥量解决数据共享问题" << std::endl;
	system("pause");
	return 0;
}

执行结果:

course one start
hello hello
DetachFunction
0
1
2
3
4
5
6
7
8
9
over!
course one end
course two start
ReferenceFunc RefF value:2
RefF: 2
*p-572662307
course two end
A class privateFunc execute!
A class execute!
course three start 互斥量解决数据共享问题
数据竞争:a_mtx value shoud be 2e6? actual a_mtx:2000000
死锁结束
lock_guard:a_mtx value shoud be 2e6? actual a_mtx:2000000
unique_lock:a_mtx value shoud be 2e6? actual a_mtx:2000000
unique_lock:a_mtx value shoud be 3? actual a_mtx:4
Log GetInstance init only execute once
21:30:41SingleTon there is error!
21:30:41SingleTon there is error!
Productor : 0
Consumer : 0
Productor : 1
Consumer : 1
Productor : 2
Consumer : 2
Productor : 3
Consumer : 3
Productor : 4
Consumer : 4
data:2000
data:1
AsyncFunc: 1000
future_reslt.get(): 1000
AsyncFunc: 1000
future_reslt1.get(): 1000
future_result2.get(): 100
course three end 互斥量解决数据共享问题
请按任意键继续. . .
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值