通过thread类编写C++多线程程序
C++语言层面thread
windows ---- createThread
linux ---- pthread_create
一:怎么创建启动一个线程
std::thread定义一个线程对象,传入线程所需要的线程函数和参数
线程自动
#include<iostream>
#include<thread>
void threadHandle1(int t)
{
std::this_thread::sleep_for(std::chrono::seconds(t));
std::cout << "hello thread1" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(t));
}
int main()
{
//定义一个线程对象,传入一个线程函数
std::thread t1(threadHandle1,3);
//主线程等待子线程结束,主线程继续往下运行
t1.join();
//把子线程设置为分离线程
t1.detach();
std::cout << "main thread done" << std::endl;
return 0;
}
二:子线程如何结束
子线程函数运行完成,线程就结束了
三:主线程如何处理子线程
t.join():等待t线程结束,当前线程继续往下运行
t.detach():把t线程设置为分离线程,主线程结束,整个进程结束,所有子线程都自动结束了!
线程间互斥-mutex互斥锁和lock_guard
C++ thread 模拟车站三个窗口卖票程序
#include<iostream>
#include<thread>
#include<list>
using namespace std;
int counts = 100;
void sellTicket(int index)
{
while (counts > 0)
{
cout << "窗口" << index<<"卖出第:"<<counts<<"张票" << endl;
counts--;
std::this_thread::sleep_for(std::chrono::microseconds(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();
}
return 0;
}

由于线程都是并行,可以看出存在一些错误。
多线程程序
存在竞态条件:多线程程序执行的结果是一致的,不会随着cpu对线程不同的调度顺序,而产生不同的运行结果
在添加互斥变量(锁)后
//全局互斥锁
std::mutex mtx;
void sellTicket(int index)
{
mtx.lock();
while (counts > 0)
{
cout << "窗口" << index<<"卖出第:"<<counts<<"张票" << endl;
counts--;
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
mtx.unlock();
}

没有任何问题了
但是仔细想想?这不是串行运算吗?等于其实只有一个窗口在卖票啊
所以是错的!
正确代码:
while (counts > 0)
{
mtx.lock();
cout << "窗口" << index<<"卖出第:"<<counts<<"张票" << endl;
counts--;
mtx.unlock();
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
当然这里也会出现其他问题,比如会出现卖0,-1张票的情形。
所以要再加一层判断
while (counts > 0)
{
mtx.lock();
if (counts > 0)
{
cout << "窗口" << index<<"卖出第:"<<counts<<"张票" << endl;
counts--;
}
mtx.unlock();
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
提问:如果程序发生了不正常运行,比如在if语句内直接return了,那么就没有unlock操作,锁就直接废了。
所以我们引入lock_guard和unique_lock
lock_guard<std::mutex> lock(mtx);
unique_lock<std::mutex> lock(mtx);
线程间的互斥=》mutex=》用lock_guard封装mutex
线程间同步通信-生产者消费者模型
多线程编程有两个问题:
一个是线程间的互斥
竞态条件=》临界区代码段=》保证原子操作=》互斥锁mutex — 轻量级的无锁机制 CAS
另一个是线程间的同步通信
示例代码:一个普通的生产者消费者模型(未添加同步通信)
#include<iostream>
#include<thread>
#include<mutex>
#include<queue>
using namespace std;
std::mutex mtx;
class QUE {
public:
void put(int val)//生产物品
{
lock_guard<std::mutex> guard(mtx);
que.push(val);
cout << "生产者 生产:" << val<<"号物品" << endl;
}
int get()//消费物品
{
lock_guard<std::mutex> guard(mtx);
int val = que.front();
que.pop();
cout << "消费者 消费:" << val << "号物品" << endl;
return val;
}
private:
queue<int> que;
};
void producer(QUE* que)
{
for (int i = 1; i <= 10; ++i)
{
que->put(i);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer(QUE* que)
{
for (int i = 1; i <= 10; ++i)
{
que->get();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main()
{
QUE que;
std::thread t1(producer,&que);
std::thread t2(consumer ,&que);
t1.join();
t2.join();
return 0;
}

可以看出,当消费者比生产者快时,消费者无法获取产品
#include<iostream>
#include<thread>
#include<mutex>
#include<queue>
#include<condition_variable>
using namespace std;
std::mutex mtx;//定义互斥锁,做线程间的互斥操作
condition_variable cv;//定义条件变量做线程间的通信
class QUE {
public:
void put(int val)//生产物品
{
lock_guard<std::mutex> guard(mtx);
if (!que.empty())
{
//que不为空,生产者应该通知消费者去消费,消费完了,再继续生产
//生产者线程应该进入阻塞状态,并且把mtx互斥锁释放掉
unique_lock<std::mutex> lck(mtx);
cv.wait(lck);
}
que.push(val);
//通知其他所有线程
cv.notify_all();
cout << "生产者 生产:" << val<<"号物品" << endl;
}
int get()//消费物品
{
lock_guard<std::mutex> guard(mtx);
while (que.empty())
{
//发现que是空的,通知生产者线程先生产物品
// 1. 进入等待状态,2把互斥锁释放
unique_lock<std::mutex> lck(mtx);
cv.wait(lck);
}
int val = que.front();
que.pop();
cout << "消费者 消费:" << val << "号物品" << endl;
return val;
}
private:
queue<int> que;
};
void producer(QUE* que)
{
for (int i = 1; i <= 10; ++i)
{
que->put(i);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer(QUE* que)
{
for (int i = 1; i <= 10; ++i)
{
que->get();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main()
{
QUE que;
std::thread t1(producer,&que);
std::thread t2(consumer ,&que);
t1.join();
t2.join();
return 0;
}
再谈lock_guard和unique_lock
unique_lock 和 condition_variable
- lock_guard和unique_lock
//利用智能指针的概念,在构造函数在能主动的获取锁
//当对象出作用域时,调用析构函数再释放
//不能使用在函数调用过程中
lock_guard<std::mutex> guard(mtx);
unique_lock只提供了右值引用的赋值和拷贝构造
可以使用在函数调用过程中
std::mutex mtx;
std::condition_variable cv;
int main()
{
//利用智能指针的概念,在构造函数在能主动的获取锁
//当对象出作用域时,调用析构函数再释放
unique_lock<std::mutex> lck(mtx);
cv.wait(lck);// =>1. 使线程进入等待状态 2.lck.unlock还可以把mtx给释放掉
- condition_variable wait和notify_all方法
cv.notify_all();
通知在cv上等待的线程,条件成立了,起来干活了
其他在cv上等待的线程,收到通知,从阻塞状态=》等待状态=》获取互斥锁=》程序继续运行
基于CAS操作的atomic原子类型
互斥锁是比较重的,如果临界区代码做的事情稍微的复杂,多。就用互斥锁
但是如果只是简单的+±-操作,可以用CAS轻量化锁来实现,
实例代码:无锁队列=》CAS实现
volatile std::atomic_bool isReady = false;
volatile std::atomic_int counts = 0;
void task()
{
while(!isReady())
{
std::this_thread::yield();//线程出让当前的cpu时间片
//等待下一次调度
}
for(int i = 0;i<100;++i)
{
counts++;
}
}
int main()
{
list<std::thread> tlist;
for(int i = 0;i<10;++i)
{
tlist.push_back(std::thread(task));
}
isReady = true;
std::this_thread::sleep_for(std::chrono::seconds(3));
for(std::thread &t : tlist)
{
t.join();
}
cout<<counts<<endl;
}
750



被折叠的 条评论
为什么被折叠?



