c++11线程总结

线程基本用法

#include<iostream>
#include <thread>
#include <chrono>

/*
C++语言级别的多线程编程=>代码可以跨平台 windows/linux/mac
thread/mutex/condition_variable
lock_gard/unique_lock(类似智能指针实现加锁和解锁)
atomic 原子类型 基于CAS操作的原子类型 线程安全的
sleep_for
C++语言程序thread:实际上windows上是用的createThread;linux上用的pthread_create,可以用strace ./a.out观察

线程内容:
一.怎样创建启动一个线程
std::thread定义一个线程对象,传入线程所需要的线程函数和参数,线程自动开启
二、子线程如何结束
子线程函数运行完成,线程就结束

三、主线程如何处理子线程
t.join():等待t线程结束,当前线程继续往下运行
t.detach():把线程t设置未分离线程,主线程结束,整个进程结束,所有子线程都自动结束了
*/
void threadHandle1(int time)
{
	//让子线程睡眠time秒
	std::this_thread::sleep_for(std::chrono::seconds(time));
	std::cout << "hello thread1!" << std::endl;
}
void threadHandle2(int time)
{
	//让子线程睡眠time秒
	std::this_thread::sleep_for(std::chrono::seconds(time));
	std::cout << "hello thread2!" << std::endl;
}
int main()
{
	//定义了一个线程对象 传入一个线程函数,新线程就开始运行了
	std::thread t1(threadHandle1,2);
	std::thread t2(threadHandle2, 3);
	//主线程等待子线程结束,主线程继续往下运行
	t1.join();	
	t2.join();
	//把子线程设置为分离线程
	//t1.detach();

	std::cout << "main theread done!" << std::endl;
	//主线程运行完成,查看如果当前进程还有未运行完成的子线程
	//进程就会异常终止
	return 0;
}

输出信息如下:

hello thread1!
hello thread2!
main theread done!

线程间互斥mutex互斥锁和lock_guard举例

#include<list>
#include<thread>
#include<iostream>
#include <mutex>
/*
C++ thread 模拟车站三个窗口买票的程序
多线程程序
竞态条件:多线程程序执行结果是一致的,不会随着CPU对线程不同的调用顺序,而产生不同的运行结果
*/
int ticketCount = 50;//车站有50张车票,由三个窗口一起卖票
std::mutex  mtx;//全局的一把互斥锁
//模拟卖票的线程函数
void sellTicket(int index)
{
	//方法一:只使用std::mutex实现
	//while (ticketCount > 0)//锁+双重判断
	//{
	//	mtx.lock();
	//	if (ticketCount > 0)
	//	{
	//		//临界区代码段=》原子操作=>线程间互斥操作=>mutex
	//		std::cout << "窗口" << index << ":卖出第" << ticketCount << "张票!" << std::endl;
	//		ticketCount--;
	//	}
	//	mtx.unlock();
	//	std::this_thread::sleep_for(std::chrono::milliseconds(100));
	//
	//}

	//方法二:使用std::lock_guard实现
	//std::lock_guard(有点类似于智能指针socped_ptr的味道)的优点:在构造函数中实现mtx.lock,在构造函数实现mtx.unlock()
	//比原始std::mutex的lock和unlock可以避免忘记unlock或者可能代码里unlock前有rentun等原因导致unlock未能执行的问题
	while (ticketCount > 0)//锁+双重判断
	{
		{//添加一层作用域,出整个作用域std::lock_guard<std::mutex> lock将调用析构函数中unlock达到释放锁的效果。
			std::lock_guard<std::mutex> lock(mtx);//保证所有的线程都能释放锁,防止死锁问题的发生
			if (ticketCount > 0)
			{
				//临界区代码段=》原子操作=>线程间互斥操作=>mutex
				std::cout << "窗口" << index << ":卖出第" << ticketCount << "张票!" << std::endl;
				ticketCount--;
			}
		}		
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}	
}
int main()
{
	std::list<std::thread> tlist;
	for (int i = 1;i <= 3;++i)
	{
		tlist.push_back(std::thread(sellTicket, i));
	}
	for (std::thread& t : tlist)
	{
		t.join();
	}
	std::cout << "所有窗口卖票结束!" << std::endl;
	return 0;
}

结果输出

窗口3:卖出第50张票!
窗口1:卖出第49张票!
窗口2:卖出第48张票!
窗口2:卖出第47张票!
窗口1:卖出第46张票!
窗口3:卖出第45张票!
窗口3:卖出第44张票!
窗口1:卖出第43张票!
窗口2:卖出第42张票!
窗口3:卖出第41张票!
窗口2:卖出第40张票!
窗口1:卖出第39张票!
窗口1:卖出第38张票!
窗口2:卖出第37张票!
窗口3:卖出第36张票!
窗口3:卖出第35张票!
窗口2:卖出第34张票!
窗口1:卖出第33张票!
窗口1:卖出第32张票!
窗口3:卖出第31张票!
窗口2:卖出第30张票!
窗口2:卖出第29张票!
窗口3:卖出第28张票!
窗口1:卖出第27张票!
窗口1:卖出第26张票!
窗口3:卖出第25张票!
窗口2:卖出第24张票!
窗口1:卖出第23张票!
窗口2:卖出第22张票!
窗口3:卖出第21张票!
窗口2:卖出第20张票!
窗口1:卖出第19张票!
窗口3:卖出第18张票!
窗口3:卖出第17张票!
窗口1:卖出第16张票!
窗口2:卖出第15张票!
窗口2:卖出第14张票!
窗口3:卖出第13张票!
窗口1:卖出第12张票!
窗口2:卖出第11张票!
窗口1:卖出第10张票!
窗口3:卖出第9张票!
窗口3:卖出第8张票!
窗口2:卖出第7张票!
窗口1:卖出第6张票!
窗口2:卖出第5张票!
窗口1:卖出第4张票!
窗口3:卖出第3张票!
窗口1:卖出第2张票!
窗口2:卖出第1张票!
所有窗口卖票结束!

线程间的同步通信举例

#include<list>
#include<thread>
#include<iostream>
#include<chrono>
#include <mutex>
#include <queue> //C++ STL所有的容器都不是线程安全的
/*
C++ 多线程编程-线程间的同步通信机制
多线程编程两个问题
1.线程间的互斥
  竞态条件=》临界区代码段=》保证原子操作=》互斥锁mutex 轻量级的无锁实现CAS
2.线程间的同步通信
生成者、消费者线程模型
*/

std::mutex mtx;//定义互斥锁,做线程间的互斥操作
std::condition_variable cv;//定义条件变量,做线程间的同步通信操作
//生产者生产一个物品,通知消费者消费一个;消费完了,消费者再通知生产者继续生产物品
class Queue
{
public:
	void put(int val)//生产物品
	{
		std::unique_lock<std::mutex> lck(mtx);
		if (!m_que.empty())
		{
			//m_que不为空,生产者应该通知消费者去消费,消费完了,再继续生产
			//生产者线程应进入等待状态,并且把mtx互斥锁释放掉
			
			cv.wait(lck);

		}
		m_que.push(val);
		/*
		notify_one:通知另外的一个线程
		notify_all:通知其它所有线程的
		通知其它所有的线程,我生产一个物品,你们赶紧消费吧
	    其他线程得到该通知,就会从等待状态=>阻塞状态=>获取互斥锁才能继续执行
		*/
		cv.notify_all();
		std::cout << "【生产者】生产:" << val << "号物品" << std::endl;
	}
	void get()//消费物品
	{		
		std::unique_lock<std::mutex> lck(mtx);
		while (m_que.empty())
		{
			//消费者线程发现m_que是空的,通知生产者先生产物品
			//#1进入等待状态 #2把互斥锁mutex释放
		
			cv.wait(lck);
			
		}
		int val = m_que.front();
		m_que.pop();
		cv.notify_all();//通知其他线程我消费完了,赶紧生产吧
		std::cout << "【消费者】消费" << val << "号物品" << std::endl;
	}

private:
	std::queue<int> m_que;
};
void producer(Queue* que)//生产者线程
{
	for (int i = 1;i <= 10;++i)
	{
		que->put(i);
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}
void consumer(Queue* que)//消费者线程
{
	for (int i = 1;i <= 10;++i)
	{
		que->get();	
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}
int main()
{
	Queue que;//两个线程共享的队列
	std::thread t1(producer,&que);
	std::thread t2(consumer, &que);
	t1.join();
	t2.join();
}

输出信息如下:

【生产者】生产:1号物品
【消费者】消费1号物品
【生产者】生产:2号物品
【消费者】消费2号物品
【生产者】生产:3号物品
【消费者】消费3号物品
【生产者】生产:4号物品
【消费者】消费4号物品
【生产者】生产:5号物品
【消费者】消费5号物品
【生产者】生产:6号物品
【消费者】消费6号物品
【生产者】生产:7号物品
【消费者】消费7号物品
【生产者】生产:8号物品
【消费者】消费8号物品
【生产者】生产:9号物品
【消费者】消费9号物品
【生产者】生产:10号物品
【消费者】消费10号物品

总结

#include<list>
#include<thread>
#include<iostream>
#include<chrono>
#include <mutex>
#include <queue> //C++ STL所有的容器都不是线程安全的

/*
unique_lock condition_variable
1.lock_guard和unique_lock
2.condition_variable wait和notify_all方法
*/
std::mutex mtx;
std::condition_variable cv;
int main()
{
	/*
	 通知在cv上等待的线程,条件成立了,起来干活了
	 其它cv上等待的线程,收到通知,从等待状态=>阻塞状态=>获取互斥锁=》线程继续往下执行
	*/
	cv.notify_all();
	//它不仅可以使用再简单的临界区段的互斥操作中,还能用再函数调用过程中
	std::unique_lock<std::mutex> lck(mtx);
	cv.wait(lck);//=>#1.使线程进入等待状态 #2.lck的unlock可以把mtx给释放掉

	//不可能用再函数参数传递或者返回过程中,只能用在简单的临界区代码段的互斥操作中
	//std::lock_guard<std::mutex> guard(mtx);
	
	/*mtx.lock();
	mtx.unlock();*/
	return 0;
}

posted @ 2025-10-18 12:33  焦涛  阅读(0)  评论(0)    收藏  举报