编译环境:Qt
join:阻塞当前线程,直到线程函数退出
detach:将线程对象与线程函数分离,线程不依赖线程对象管理
注:join和detach两者必选其一,否则线程对象的回收会影响线程的回收,导致程序崩溃
思考:执行detach之后的线程如何退出?
- 方法:设置标志位
#include "dialog.h"
#include<iostream>
using namespace std;
#include <QDebug>
#include<thread>
#include <QApplication>
int add(int a,int b)
{
qDebug()<<"add";
qDebug()<<"a+b = "<<a+b;
_sleep(1000);
return a+b;
}
class AA
{
public:
AA(){
//在构造函数中创建线程
//如果使用_beginthreadex 线程函数是static
thread th(&threadFun,this);
th.detach();
}
//执行detach之后线程如何退出
//设置标志位
void threadFun(){
while(!m_isQuit)
{
_sleep(100);
qDebug()<<"do some work";
}
}
private:
bool m_isQuit = false;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
{
thread th(&add,3,4);//线程函数可以以参数形式传入
//join函数 阻塞当前线程 直到线程函数退出
// _sleep(100);
// qDebug()<<"before join";
// th.join();
// qDebug()<<"thread join";
//detach 将线程对象与线程函数分离,线程不依赖线程对象管理
qDebug()<<"before detach";
th.detach();
qDebug()<<"after detach";
//注:join和detach两者必选其一,否则线程对象的回收会影响线程的回收,导致程序崩溃
}
Dialog w;
w.show();
return a.exec();
}
看下面一段代码:
执行结果不等于300,为什么?
- ++g_value时分为三步:读,自增,写。在多线程情况下,可能存在多个线程读或写同一个值的情况,这就使得结果小于我们预期的值了。
线程并发引发的数据问题:
并发:同一时间间隔内,程序交替执行
解决:线程同步
线程同步:同一时间,只允许一个线程访问资源
实现线程同步方法:
- 互斥锁
- 读写锁
- 条件变量
- 原子操作
- 信号和槽
- 事件循环
1、互斥锁
lock_guard 与 unique_lock 管理互斥锁,让互斥锁使用更方便、更安全(可以避免死锁,比如我们使用完锁忘记释放了)
lock_guard 与 unique_lock 遵循RAII
- RAII,资源获取即初始化,是C++很重要的思想。
思考:如果需要在中途释放锁,怎么办?
- 使用 unique_lock
- 使用 lock_guard + 花括号实现,即锁一部分
锁锁定代码长度称为粒度,锁定的代码长度越长,锁的粒度越大,影响并发的效率。
2、条件变量
#include<condition_variable>
std::condition_variable con_var;con_var.notify_one();(通知一个)
con_var.notify_all();(通知所有)
con_var.wait();
wait() 函数:
- 有两个参数:第一个参数:是一个已经上锁的互斥锁(unique_lock),第二个参数:是一个可调用对象,其中包含函数指针,仿函数,bind ,lambda表达式
- 如果这个函数执行返回值是false ,就会通知无效,可以避免误通知
- 作用:阻塞当前线程,直到收到通知 notify_one notify_all
- 当调用wait时,释放互斥锁,阻塞当前线程,将线程放入条件变量等待的容器中
- 当收到通知时,获取互斥锁,执行后续代码
- wait()和通知的关系:在使用的时候,一定是wait之后才能收到通知,否则会失效
运行结果:
先打印出三个"before wait",之后每点击一下按钮,打印一个"after wait"
举例:
现在我们改变一下:
运行结果:
我们第一次按下按钮,quit取非为真,此时打印"after wait",再次按下,此时quit取非为假,此时无反应,第三次按下,此时quit又为真,打印"after wait"......
3、原子操作
#include<atomic>
atomic<int> cnt(0);
- cnt++;
- cnt--;
- cnt.load();
#include "dialog.h"
#include<iostream>
using namespace std;
#include <QDebug>
#include<thread>
#include <QApplication>
#include<atomic>
atomic<int> cnt(0);
void AutomicFunc()
{
for(int i=0;i<100;i++)
{
_sleep(10);
//cnt++; //加锁的方式进行++,是原子操作,线程安全
cnt = cnt + 1; //非原子操作,线程不安全
_sleep(10);
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
{
thread th[3];
for(int i=0;i<3;i++)
{
th[i] = thread(&AutomicFunc);
}
for(int i=0;i<3;i++)
{
th[i].join();
}
}
qDebug()<<"count = "<<cnt.load();//取值
Dialog w;
w.show();
return a.exec();
}
如有问题,欢迎交流指正!