QT线程

2025-12-23 12:13:12
文章摘要
在Linux中有多线程的api,在qt中也有对应的多线程api,但是这个api是对系统的api进行封装的,在C++11中,就引入了std::thread,如果需要进行比较 也就是(Linux的api<std::thread<Qt的api)。Qt的多线程的api是参考了java的api设计方式

@TOC

介绍

在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-&gt;centralWidget());
 //创建显示器
QLCDNumber* nb = new QLCDNumber();
nb-&gt;resize(100,200);

//创建开始按钮
QPushButton* button = new QPushButton();
button-&gt;resize(300,400);
button-&gt;setText(&quot;开始&quot;);

layou-&gt;addWidget(nb,1);
layou-&gt;addWidget(button,1);

//获取中心窗口
this-&gt;centralWidget()-&gt;setLayout(layout());
thread = new threead();
connect(button,&amp;QPushButton::clicked,this,=
{
thread-&gt;start();
});
connect(thread,&amp;threead::currenTime,this,[=](const QString &amp;time)
{
nb-&gt;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):通知所有在条件变量上等待的线程。

声明:该内容由作者自行发布,观点内容仅供参考,不代表平台立场;如有侵权,请联系平台删除。