作者前言
🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言、C++和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂
线程
介绍
在Linux中有多线程的api,在qt中也有对应的多线程api,但是这个api是对系统的api进行封装的,在C++11中,就引入了std::thread,如果需要进行比较 也就是(Linux的api<std::thread<Qt的api)。Qt的多线程的api是参考了java的api设计方式
QThread
需要创建对应的对象,在创建对象的时候,需要重点指定入口函数,也就是使用QThread创建对象的时候,需要对run函数进行重写
API
下面是Qthread提供的一些常用的api:


前面我们使用了定时器来实现一个时间显示器, 我们这次使用这个线程来实现时间显示器
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
layou = new QVBoxLayout(this->centralWidget());
//创建显示器
QLCDNumber* nb = new QLCDNumber();
nb->resize(100,200);
//创建开始按钮
QPushButton* button = new QPushButton();
button->resize(300,400);
button->setText("开始");
layou->addWidget(nb,1);
layou->addWidget(button,1);
//获取中心窗口
this->centralWidget()->setLayout(layout());
thread = new threead();
connect(button,&QPushButton::clicked,this,[=]()
{
thread->start();
});
connect(thread,&threead::currenTime,this,[=](const QString &time)
{
nb->display(time);
});
}
/********thread。cpp******/
threead::threead()
:QThread()
{
}
void threead::run()
{
//写一个死循环
while (1)
{
QTime* time = new QTime();
emit currenTime(time->currentTime().toString("y=M=d-H:m"));
sleep(1);
}
}
效果图:

注意:
statu函数执行的时候,就是创建线程的过程,这个过程就会调用到run函数,无需我们调用,
还有就是,创建的线程无法直接更改主线程的界面的内容, 只能通过主线程进行修改
线程安全
线程安全是指在多线程环境中,多个线程并发访问共享资源时,不会导致数据不一致或错误的状态。一般的情况就是增加一个锁,以防数据被同时进行修改。
互斥锁(Mutex)
特点:QMutex 是 Qt 框架提供的互斥锁类,⽤于保护共享资源的访问,实现线程间的互斥操作。
⽤途:在多线程环境下,通过互斥锁来控制对共享数据的访问,确保线程安全。
下面写一个多进程访问一个变量进行更改的代码
锁的写法:
QMutex mutex;
mutex.lock(); //上锁
//访问共享资源
//...
mutex.unlock(); //解锁
/*****addnum。cpp***/
void addnum::run()
{
while(1)
{
mutex->lock();
qDebug()<< "当前的值为"<< num;
qDebug()<< "当前的进程为"<<this->currentThreadId();
num++;
mutex->unlock();
sleep(1);
}
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
addnum * thread_One = new addnum();
addnum * thread_Two = new addnum();
thread_One->start();
thread_Two->start();
// thread_One->wait();
// thread_Two->wait();
qDebug()<<addnum::num;
}
上面的代码有两种运行结果, 一个是直接输出主线程的,然后再输出其他线程的(有注释掉wait函数),另一种是输出其他线程的,最后再输出主线程的,
图一:

图二:

可以看清楚,原因是这个代码中的线程有三个, 分别是主线程和thread_Two 以thread_One,这三个线程是并发的,输出的结果就会有错, 还有另一个方面就是没有添加锁的情况, 例如thread_Two 和thread_One在运行的时候,thread_Two 刚好把num从而2变成3,而与此同时thread_One拿到的num的值刚好是2.也刚好完成变成3的过程,这里就会有线程安全问题
QMutexLocker
简化对互斥锁的上锁和解锁操作,避免忘记解锁导致的死锁等问题。简单的说。就是前面写的代码有时候会忘记解锁,就会造成线程阻塞,进行导致进程瘫痪,这个类可以进一步的解决这个问题
写法:
QMutex mutex;
{
QMutexLocker locker(&mutex); //在作⽤域内⾃动上锁
//访问共享资源
}//在作⽤域结束时⾃动解锁
所以我们可以更改代码为
void addnum::run()
{
while(1)
{
// mutex->lock();
QMutexLocker locker(mutex);
qDebug()<< "当前的值为"<< num;
qDebug()<< "当前的进程为"<<this->currentThreadId();
num++;
// mutex->unlock();
sleep(1);
}
}
这个QMutexLocker这个写法, 不一定需要多写{}这个,也可以在一些场合上进行套用
除了有这个锁之外,还有一些其他的锁
QReadWriteLocker、QReadLocker、QWriteLocker
QReadWriteLock 是读写锁类,⽤于控制读和写的并发访问。
QReadLocker ⽤于读操作上锁,允许多个线程同时读取共享资源。
QWriteLocker ⽤于写操作上锁,只允许⼀个线程写⼊共享资源。
⽤途:在某些情况下,多个线程可以同时读取共享数据,但只有⼀个线程能够进⾏写操作。读写锁提供了更⾼效的并发访问⽅式。
写法:
QReadWriteLock rwLock;
//在读操作中使⽤读锁
{
QReadLocker locker(&rwLock); //在作⽤域内⾃动上读锁
//读取共享资源
//...
} //在作⽤域结束时⾃动解读锁
//在写操作中使⽤写锁
{
QWriteLocker locker(&rwLock); //在作⽤域内⾃动上写锁
//修改共享资源
//...
} //在作⽤域结束时⾃动解写锁
条件变量
在多线程编程中,假设除了等待操作系统正在执⾏的线程之外,某个线程还必须等待某些条件满⾜才能执⾏,这时就会出现问题。这种情况下,线程会很⾃然地使⽤锁的机制来阻塞其他线程,因为这只是线程的轮流使⽤,并且该线程等待某些特定条件,⼈们会认为需要等待条件的线程,在释放互斥锁或读写锁之后进⼊了睡眠状态,这样其他线程就可以继续运⾏。当条件满⾜时,等待条件的线程将被另⼀个线程唤醒。
简单的说就是条件变量通常与互斥锁(mutex)结合使用。当一个线程需要等待某个条件时,它会释放互斥锁并进入等待状态,直到另一个线程通知它条件已满足。
条件变量主要有以下几种操作:
等待(wait):线程在条件变量上等待,同时释放与条件变量相关联的互斥锁。
通知(notify):当条件满足时,线程可以通知一个或多个在条件变量上等待的线程,使它们恢复执行。
通知所有(notifyAll):通知所有在条件变量上等待的线程。