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;
}
浙公网安备 33010602011771号