【QT】线程控制和同步

目录

概述

QThread常用API

线程使用

创建一个QThread的子类

主线程启动线程

线程安全

互斥锁

QMutex

使用示例:两个线程一个共享静态变量进行++

线程子类创建

主线程调用

QMutexLocker

条件变量 

信号量 


概述

  • 在 Qt 中,多线程的处理⼀般是通过 QThread类 来实现。
  • QThread 代表⼀个在应⽤程序中可以独⽴控制的线程,也可以和进程中的其他线程共享数据。
  • QThread 对象管理程序中的⼀个控制线程。

        QThread要想创建线程,就需要创建出这样类的实例,创建线程的时候,需要重点指定线程的入口函数,创建一个QThread的子类,重写其中的run函数,起到指定入口函数的方式。 

QThread常用API

run()
线程的⼊⼝函数..
start()
通过调⽤ run() 开始执⾏线程。操作系统将根据优先级参数调度线程。如果线程已经在运⾏,这个函数什么也不做。
currentThread()
返回⼀个指向管理当前执⾏线程的 QThread的指针。
isRunning()
如果线程正在运⾏则返回true;否则返回false。
sleep() / msleep() / usleep()
使线程休眠,单位为秒 / 毫秒 / 微秒
wait()
阻塞线程,直到满⾜以下任何⼀个条件:
与此 QThread 对象关联的线程已经完成执⾏(即当它从run()返回时)。如果线程已经完成,这个函数将返回 true。如果线程尚未启动,它也返回 true。
已经过了⼏毫秒。如果时间是 ULONG_MAX(默认值),那么等待永远不会超时(线程必须从run()返回)。如果等待超时,此函数将返回 false。这提供了与 POSIX pthread_join() 函数类似的功能
terminate()
终⽌线程的执⾏。线程可以⽴即终⽌,也可以不⽴即终⽌,这取决于操作系统的调度策略。在terminate() 之后使⽤ QThread::wait() 来确保。
finished()
当线程结束时会发出该信号,可以通过该信号来实现线程的清理⼯作。

线程使用

        由于存在线程安全的问题,多个线程同时对于界面的状态进行修改,此时就会导致界面出错了,因此在qt中,多线程内部不允许对界面的控件状态进行修改,只能在主线程中进行修改。

创建一个QThread的子类

thread.h

#ifndef THREAD_H
#define THREAD_H
#include <QThread>
#include <QWidget>

class Thread : public QThread
{
    Q_OBJECT
public:
    Thread();
    void run();
signals:
    void notify();
};

#endif // THREAD_H

thread.cpp

#include "thread.h"

Thread::Thread()
{

}

void Thread::run()
{
    for(int i = 0 ; i < 10; i++)
    {
        sleep(1);
        emit notify();
    }
}

主线程启动线程

widget.h

#ifndef WIDGET_H
#define WIDGET_H
#include "thread.h"
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void handle();
private:
    Ui::Widget *ui;
    Thread thread;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    connect(&thread,&Thread::notify,this,&Widget::handle);

    thread.start();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handle()
{
    int value = ui->lcdNumber->intValue();
    ui->lcdNumber->display(--value);
}

        客户端中的多线程,主要用于通过多线程的方式,执行一些耗时的等待IO的操作,避免主线程被卡死,比如当用户需要上传/下载一个很大的文件,我们可以使用单独的线程,来处理这种密集的IO操作,要挂起也是挂起这个新的线程,主线程负责事件循环,负责处理用户的各种操作。

线程安全

实现线程互斥和同步常⽤的类有:
  • 互斥锁:QMutex、QMutexLocker
  • 条件变量:QWaitCondition
  • 信号量:QSemaphore
  • 读写锁:QReadLocker、QWriteLocker、QReadWriteLock

互斥锁

        互斥锁是⼀种保护和防⽌多个线程同时访问同⼀对象实例的⽅法,在 Qt 中,互斥锁主要是通过 QMutex类来处理。QMutex中lock加锁,unlock解锁。

QMutex

特点:QMutex 是 Qt 框架提供的互斥锁类,⽤于保护共享资源的访问,实现线程间的互斥操作。
⽤途:在多线程环境下,通过互斥锁来控制对共享数据的访问,确保线程安全。

使用示例:两个线程一个共享静态变量进行++

线程子类创建

thread.h

#ifndef THREAD_H
#define THREAD_H
#include <QThread>
#include <QWidget>
#include <QMutex>
class Thread : public QThread
{
    Q_OBJECT
public:
    Thread();
    void run();
    //共享资源
    static int num;
    //创建一把锁
    QMutex mutex;
signals:
    void notify();
};

#endif // THREAD_H

thread.cpp

#include "thread.h"

int Thread::num=0;
Thread::Thread()
{

}

void Thread::run()
{
    for(int i = 0 ; i < 10; i++)
    {
        mutex.lock();
        num++;
        mutex.unlock();
    }
}
主线程调用
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    Thread::num=0;
    Thread t1,t2;
    t1.start();
    t2.start();

    //让主线程等待两个线程执行完毕
    t1.wait();
    t2.wait();

    qDebug()<<Thread::num;
}

Widget::~Widget()
{
    delete ui;
}

QMutexLocker

上述的代码中,在获取到锁后,执行我们指定的逻辑,一旦发生错误,或者异常将会导致锁未释放,因此我们需要引入 QMutexLocker locker(&mutex);

        特点:QMutexLocker 是 QMutex 的辅助类,使⽤ RAII(Resource Acquisition Is Initialization)⽅式 对互斥锁进⾏上锁和解锁操作。
        ⽤途:简化对互斥锁的上锁和解锁操作,避免忘记解锁导致的死锁等问题。

代码示例: 

void Thread::run()
{
    for(int i = 0 ; i < 10; i++)
    {
        QMutexLocker locker(&mutex); //在作⽤域内⾃动上锁
        num++;
    }//在作⽤域结束时⾃动解锁
}

此外还有QReadWriteLocker、QReadLocker、QWriteLocker

特点:

  • QReadWriteLock 是读写锁类,⽤于控制读和写的并发访问。
  • QReadLocker ⽤于读操作上锁,允许多个线程同时读取共享资源。
  • QWriteLocker ⽤于写操作上锁,只允许⼀个线程写⼊共享资源。
  • ⽤途:在某些情况下,多个线程可以同时读取共享数据,但只有⼀个线程能够进⾏写操作。读写锁提 供了更⾼效的并发访问⽅式。

条件变量 

在多线程编程中,假设除了等待操作系统正在执⾏的线程之外,某个线程还必须等待某些条件满⾜才能执⾏,这时就会出现问题。这种情况下,线程会很⾃然地使⽤锁的机制来阻塞其他线程,因为这只是线程的轮流使⽤,并且该线程等待某些特定条件,⼈们会认为需要等待条件的线程,在释放互斥锁或读写锁之后进⼊了睡眠状态,这样其他线程就可以继续运⾏。当条件满⾜时,等待条件的线程将被另⼀个线程唤醒。
在 Qt 中,专⻔提供了 QWaitCondition类 来解决像上述这样的问题。
  • 特点:QWaitCondition 是 Qt 框架提供的条件变量类,⽤于线程之间的消息通信和同步。
  • ⽤途:在某个条件满⾜时等待或唤醒线程,⽤于线程的同步和协调

也就是说多个线程之间的调度是无序的,为了能够一定程度的干预线程之间的执行顺序,需要引入条件变量。

QMutex mutex;
QWaitCondition condition;
// 在等待线程中
mutex. lock ();
// 检查条件是否满⾜,若不满⾜则等待
while (! conditionFullfilled ())
{
        condition. wait (&mutex); // 等待条件满⾜并释放锁
}
// 条件满⾜后继续执⾏
//...
mutex. unlock ();
// 在改变条件的线程中
mutex. lock ();
// 改变条件
changeCondition ();
condition. wakeAll (); // 唤醒等待的线程
mutex. unlock ();

信号量 

有时在多线程编程中,需要确保多个线程可以相应的访问⼀个数量有限的相同资源。例如,运⾏程序 的设备可能是⾮常有限的内存,因此我们更希望需要⼤量内存的线程将这⼀事实考虑在内,并根据可 ⽤的内存数量进⾏相关操作,多线程编程中类似问题通常⽤信号量来处理。信号量类似于增强的互斥锁,不仅能完成上锁和解锁操作,⽽且可以跟踪可⽤资源的数量。
  • 特点:QSemaphore 是 Qt 框架提供的计数信号量类,⽤于控制同时访问共享资源的线程数量。
  • ⽤途:限制并发线程数量,⽤于解决⼀些资源有限的问题。

QSemaphore semaphore ( 2 ); // 同时允许两个线程访问共享资源
// 在需要访问共享资源的线程中
semaphore. acquire (); // 尝试获取信号量,若已满则阻塞
// 访问共享资源
//...
semaphore. release (); // 释放信号量
// 在另⼀个线程中进⾏类似操作
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值