欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > 【QT】线程控制和同步

【QT】线程控制和同步

2024/10/24 10:14:57 来源:https://blog.csdn.net/lzb_kkk/article/details/140484420  浏览:    关键词:【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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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 (); // 释放信号量
// 在另⼀个线程中进⾏类似操作

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com