目录
- 通过thread类编写C++多线程程序
- 线程间互斥——mutex互斥锁和lock_guard
- mutex互斥锁
- lock_guard
- 线程间通信
- C++11实现生产者与消费者模型
- 基于CAS操作的atomic原子类型
橙色
通过thread类编写C++多线程程序
为什么结果没有子线程中所打印的字符串呢?因为通过detach进行了线程分离,即主线程不会等待子线程结束再继续往下运行,而是直接运行到底,所以主线程运行完直接结束了。而子线程是睡眠了两秒后才打印语句,此时主线程已经结束,也就看不到该语句了
#include<stdio.h>
#include<iostream>
#include<thread>
using namespace std;
/*
线程内容:
一.怎么创建启动一个线程
std::thread定义一个线程对象,传入线程所需要的线程函数和参数,线程自动开启
t.join():等待t线程结束,当前线程继续往下运行
t.detach():把t线程设置为分离线程,主线程结束,整个进程结束,所有子线程都自动结束了
*/
void threadHandele1(int time)
{
//让子线程睡眠2秒
std::this_thread::sleep_for(std::chrono::seconds(time));
cout << "hello thread1!" << endl;
}
int main(){
//创建了一个线程对象,传入一个线程函数,新线程就开始运行了
thread t1(threadHandele1,2);
//主线程等待子线程结束,主线程继续往下运行
//t1.join();
//把子线程设置为分离线程
t1.detach();
cout << "main thread done!" << endl;
return 0;
}
线程间互斥——mutex互斥锁和lock_guard
mutex互斥锁
很值得注意的是锁+双重判断的应用。为什么要在锁上后再加上一个if语句判断呢?因为如果不加的话,当ticketCount=1时,第一个线程拿到锁,开始执行,但此时可能还没执行到ticketCount–,所以其他子线程也进入while循环,并在mtx.lock()处阻塞,此时第一个线程卖出票了并释放锁,于是已经进入while循环的第二个线程开始拿锁执行,就容易出现卖出第0张票,卖出第-1张票的情况,所以在拿到锁后再加入一个if语句判断是十分有必要的
#include<stdio.h>
#include<iostream>
#include<thread>
#include<mutex>
#include<list>
using namespace std;
/*
C++ thread 模拟车站三个窗口卖票的程序
*/
int ticketCount = 100;//车站有100张票,由三个窗口一起卖票
std::mutex mtx;//全局的一把互斥锁
//模拟卖票的线程函数
void sellTicket(int index)
{
while(ticketCount>0)//ticketCount=1 锁+双重判断
{
mtx.lock();
if(ticketCount>0)
{
cout << "窗口:" << index << "卖出第:" << ticketCount <<"张票"<< endl;
ticketCount--;
}
mtx.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main()
{
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();
}
cout << "所有窗口卖票结束!" << endl;
return 0;
}
lock_guard
lock_guard是一个模板类
#include<stdio.h>
#include<iostream>
#include<thread>
#include<mutex>
#include<list>
using namespace std;
/*
C++ thread 模拟车站三个窗口卖票的程序
*/
int ticketCount = 100;//车站有100张票,由三个窗口一起卖票
std::mutex mtx;//全局的一把互斥锁
//模拟卖票的线程函数
void sellTicket(int index)
{
while(ticketCount>0)//ticketCount=1 锁+双重判断
{
{
//出了{}作用域就会析构自动释放mtx这把锁,保证所有线程都能释放锁
//防止死锁问题的发生
lock_guard<std::mutex> lock(mtx);
if(ticketCount>0)
{
cout << "窗口:" << index << "卖出第:" << ticketCount <<"张票"<< endl;
ticketCount--;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main()
{
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();
}
cout << "所有窗口卖票结束!" << endl;
return 0;
}
线程间通信
C++11实现生产者与消费者模型
#include <mutex>
#include <condition_variable>
#include <queue> //C++ STL里面的所有容器都不是线程安全的
#include <iostream>
#include <thread>
using namespace std;
class Queue
{
public:
void put(int val) // 生产者
{
unique_lock<std::mutex> lck(mtx);
while (!que.empty())
{
// que不为空,生产者应该通知消费者去消费
// 生产者应该进入 #1等待状态 #2并把mtx互斥锁释放掉
cv.wait(lck);
}
que.push(val);
cv.notify_all(); // 通知其他所有消费者可以进行消费了
//其他线程的得到通知就会从等待状态 ==> 阻塞状态 ==> 获取互斥锁才能继续执行
cout << "生产者生产:" << val << "号物品" << endl;
}
int get() // 消费者
{
unique_lock<std::mutex> lck(mtx);
while (que.empty())
{
// 消费者线程发现que是空的,通知生产者线程生产物品
// #1 进入等待状态 #2把互斥锁mutex释放掉
cv.wait(lck);
}
int val = que.front();
que.pop();
cv.notify_all(); //通知其他所有生产者可以进行生产了
cout << "消费者消费: " << val << "号物品" << endl;
return val;
}
private:
queue<int> que;
mutex mtx; // 定义互斥锁,做线程间的胡吃操作
condition_variable cv; // 定义条件变量,做线程之间的同步通信操作
};
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; // 两个线程共享的队列
thread t1(producer, &que);
thread t2(consumer, &que);
t1.join();
t2.join();
}
unique_lock与lock_guard的比较,仔细看下面的代码和注释
#include <bits/stdc++.h>
#include <mutex>
#include <condition_variable>
using namespace std;
std::mutex mtx;
std::condition_variable cv;
int main()
{
/***
* 唤醒在cv上等待的线程
* 其他在cv上等待的线程收到通知,从等待状态 ==> 阻塞状态 ==> 获得互斥锁 ==> 线程继续向下执行
* **/
// cv.notify_all();
// 它不仅可以用在简单的临界区代码段的互斥操作中,还能用于函数调用过程中
unique_lock<std::mutex> lck(mtx);
cv.wait(lck); // #两个作用:1使线程进入等待状态 2 lck.unlock()可以把mutex释放掉
// 不可以用在函数参数传递或者返回过程中,只能用在简单的临界区代码段的互斥操作中
lock_guard<std::mutex> guard(mtx);
// mtx.lock();
// mtx.unlock();
return 0;
}
基于CAS操作的atomic原子类型
如果mycount不是原子类型的话,最后的值很可能就不会是1000了
#include <iostream>
#include <thread>
#include<atomic>
#include<list>
using namespace std;
atomic_bool isReady;
atomic_int mycount;
void task()
{
while(!isReady)
{
this_thread::yield();//线程出让当前的cpu时间片,等待下一次调度
}
for (int i = 0; i < 100;i++)
{
mycount++;
}
}
int main()
{
list<std::thread> tlist;
isReady=false;
mycount = 0;
for (int i = 0; i < 10;i++)
{
tlist.push_back(std::thread(task));
}
this_thread::sleep_for(chrono::seconds(3));
isReady = true;
for(thread &t:tlist)
{
t.join();
}
cout << "mycount:" << mycount << endl;
}