QT第五课------QT系统相关------线程

作者前言

🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂

​🎂 作者介绍: 🎂🎂

🎂 🎉🎉🎉🎉🎉🎉🎉 🎂

🎂作者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):通知所有在条件变量上等待的线程。

相关推荐
lkbhua莱克瓦246 小时前
IO练习——网络爬虫(爬取数据)
java·开发语言·爬虫·io流练习·java练习
net3m336 小时前
雅特力单片机用串口USART_INT_TDE中断比用USART_INT_TRAC的 发送效率要高
java·开发语言·算法
爱打代码的小林6 小时前
python基础(逻辑回归例题)
开发语言·python·逻辑回归
一过菜只因6 小时前
JavaWeb后端(spring--boot)
java·开发语言
五仁火烧6 小时前
安装rust开发环境
开发语言·后端·rust
Yue丶越6 小时前
【C语言】动态内存管理
c语言·开发语言
Edward111111116 小时前
普通java项目转为maven项目 J文件后缀.java变C文件
java·开发语言·maven
赵谨言6 小时前
基于OpenCV的图像梯度与边缘检测研究
大数据·开发语言·经验分享·python
莓莓儿~6 小时前
Next.js 14 App Router数据获取开发手册
开发语言·前端·javascript