目录
  • 一、线程基础
    • 1、GUI线程与工作线程
    • 2、数据的同步访问
  • 二、QT多线程简介
    • 三、QThread线程
      • 1、QThread线程基础
      • 2、线程的优先级
      • 3、线程的创建
      • 4、线程的执行
      • 5、线程的退出
      • 6、线程的等待
      • 7、线程的状态
      • 8、线程的属性
      • 9、线程与事件循环
    • 四、线程的同步
      • 1、线程同步基础
      • 2、互斥量QMutex
      • 3、互斥锁QMutexLocker
      • 4、QReadWriteLock
      • 5、QReadLocker和QWriteLocker
      • 6、信号量QSemaphore
      • 7、等待条件QWaitCondition
      • 8、高级事件队列
    • 五、可重入与线程安全
      • 1、可重入
      • 2、线程安全
    • 六、线程与信号槽
      • 1、线程的依附性
      • 2、QObject与线程
      • 3、线程的事件循环
      • 4、其他线程访问QObject子类
      • 5、跨线程的信号槽
    • 七、线程的设计
      • 1、线程的生命周期
      • 2、同步线程类设计
      • 3、异步线程类设计
    • 八、线程的使用方式
      • 1、子类化QThread
      • 2、Worker-Object
    • 九、多线程与GUI组件的通信
      • 1、多线程与GUI组件通信基础
      • 2、信号槽方式
      • 3、发送自定义事件方式

    一、线程基础

    1、GUI线程与工作线程

    每个程序启动后拥有的第一个线程称为主线程,即GUI线程。QT中所有的组件类和几个相关的类只能工作在GUI线程,不能工作在次线程,次线程即工作线程,主要负责处理GUI线程卸下的工作。

    2、数据的同步访问

    二、QT多线程简介

    • QThread 提供了跨平台的多线程解决方案
    • QThreadStorage 提供逐线程数据存储
    • QMutex 提供相互排斥的锁,或互斥量
    • QMutexLocker 是一个辅助类,自动对 QMutex 加锁与解锁
    • QReadWriterLock 提供了一个可以同时读操作的锁
    • QReadLocker与QWriteLocker 自动对QReadWriteLock 加锁与解锁
    • QSemaphore 提供了一个整型信号量,是互斥量的泛化
    • QWaitCondition 提供了一种方法,使得线程可以在被另外线程唤醒之前一直休眠。

    三、QThread线程

    1、QThread线程基础

        QThread是Qt线程中有一个公共的抽象类,所有的线程类都是从QThread抽象类中派生的,需要实现QThread中的虚函数run(),通过start()函数来调用run函数。
        void run()函数是线程体函数,用于定义线程的功能。
        void start()函数是启动函数,用于将线程入口地址设置为run函数。
        void terminate()函数用于强制结束线程,不保证数据完整性和资源释放。
        QCoreApplication::exec()总是在主线程(执行main()的线程)中被调用,不能从一个QThread中调用。在GUI程序中,主线程也称为GUI线程,是唯一允许执行GUI相关操作的线程。另外,必须在创建一个QThread前创建QApplication(or QCoreApplication)对象。
        当线程启动和结束时,QThread会发送信号started()和finished(),可以使用isFinished()和isRunning()来查询线程的状态。
        从Qt4.8起,可以释放运行刚刚结束的线程对象,通过连接finished()信号到QObject::deleteLater()槽。
        使用wait()来阻塞调用的线程,直到其它线程执行完毕(或者直到指定的时间过去)。
        静态函数currentThreadId()和currentThread()返回标识当前正在执行的线程。前者返回线程的ID,后者返回一个线程指针。
        要设置线程的名称,可以在启动线程之前调用setObjectName()。如果不调用setObjectName(),线程的名称将是线程对象的运行时类型(QThread子类的类名)。

    2、线程的优先级

     QThread::IdlePriority 0 scheduled only when no other threads are running. QThread::LowestPriority 1 scheduled less often than LowPriority. QThread::LowPriority 2 scheduled less often than NormalPriority. QThread::NormalPriority 3 the default priority of the operating system. QThread::HighPriority 4 scheduled more often than NormalPriority. QThread::HighestPriority 5 scheduled more often than HighPriority. QThread::TimeCriticalPriority 6 scheduled as often as possible. QThread::InheritPriority 7 use the same priority as the creating thread. This is the default. void setPriority(Priority priority) 

    3、线程的创建

     void start ( Priority priority = InheritPriority ) 

    4、线程的执行

     int exec() [protected] 

     void run() [virtual protected] 

    5、线程的退出

     void quit(); 

     void exit ( int returnCode = 0 ); 

     void terminate (); 

    terminate是否调用取决于setTerminationEnabled ( bool enabled = true )开关。

     void requestInterruption() 

    通过在线程类中增加标识变量volatile bool m_stop,通过m_stop变量的值判断run函数是否执行结束返回。

     #ifndef WORKTHREAD_H #define WORKTHREAD_H #include <QThread> #include <QDebug> class WorkThread : public QThread { protected: //线程退出的标识量 volatile bool m_stop; void run() { qDebug() << "run begin"; while(!m_stop) { //task handling int* p = new int[1000]; for(int i = 0; i < 1000; i++) { p[i] = i * i; } sleep(2); delete [] p; } qDebug() << "run end"; } public: WorkThread() m_stop = false; //线程退出的接口函数,用户使用 void stop() m_stop = true; }; #endif // WORKTHREAD_H 

    6、线程的等待

     bool wait ( unsigned long time = ULONG_MAX )

     void msleep ( unsigned long msecs ) void sleep ( unsigned long secs ) void usleep ( unsigned long usecs )

    7、线程的状态

    bool isFinished () const 线程是否已经退出
    bool isRunning () const   线程是否处于运行状态

    8、线程的属性

     Priority priority () const void setPriority ( Priority priority ) uint stackSize () const void setStackSize ( uint stackSize ) void setTerminationEnabled ( bool enabled = true ) 

    9、线程与事件循环

    四、线程的同步

    1、线程同步基础

        临界资源:每次只允许一个线程进行访问的资源
        线程间互斥:多个线程在同一时刻都需要访问临界资源
        线程锁能够保证临界资源的安全性,通常,每个临界资源需要一个线程锁进行保护。
        线程死锁:线程间相互等待临界资源而造成彼此无法继续执行。
        产生死锁的条件:
        A、系统中存在多个临界资源且临界资源不可抢占
        B、线程需要多个临界资源才能继续执行
        死锁的避免:
        A、对使用的每个临界资源都分配一个唯一的序号
        B、对每个临界资源对应的线程锁分配相应的序号
        C、系统中的每个线程按照严格递增的次序请求临界资源
        QMutex, QReadWriteLock, QSemaphore, QWaitCondition 提供了线程同步的手段。使用线程的主要想法是希望它们可以尽可能并发执行,而一些关键点上线程之间需要停止或等待。例如,假如两个线程试图同时访问同一个全局变量,结果可能不如所愿。

    2、互斥量QMutex

    头文件声明:    #include <QMutex>
    互斥量声明:    QMutex m_Mutex;
    互斥量加锁:    m_Mutex.lock();
    互斥量解锁:    m_Mutex.unlock();

     QMutex ( RecursionMode mode = NonRecursive ) 

    A、Recursive
        一个线程可以对mutex多次lock,直到相应次数的unlock调用后,mutex才真正被解锁。

    B、NonRecursive
        默认模式,mutex只能被lock一次。
        如果使用了Mutex.lock()而没有对应的使用Mutex.unlcok()的话就会造成死锁,其他的线程将永远也得不到接触Mutex锁住的共享资源的机会。尽管可以不使用lock()而使用tryLock(timeout)来避免因为死等而造成的死锁( tryLock(负值)==lock()),但是还是很有可能造成错误。

        bool tryLock();
        如果当前其他线程已对该mutex加锁,则该调用会立即返回,而不被阻塞。
        bool tryLock(int timeout);
        如果当前其他线程已对该mutex加锁,则该调用会等待一段时间,直到超时

     QMutex mutex; int complexFunction(int flag) { mutex.lock(); int retVal = 0; switch (flag) { case 0: case 1: mutex.unlock(); return moreComplexFunction(flag); case 2: { int status = anotherFunction(); if (status < 0) { mutex.unlock(); return -2; } retVal = status + flag; } break; default: if (flag > 10) { mutex.unlock(); return -1; } mutex.unlock(); return retVal; } 

    3、互斥锁QMutexLocker

        在较复杂的函数和异常处理中对QMutex类mutex对象进行lock()和unlock()操作将会很复杂,进入点要lock(),在所有跳出点都要unlock(),很容易出现在某些跳出点未调用unlock(),所以Qt引进了QMutex的辅助类QMutexLocker来避免lock()和unlock()操作。在函数需要的地方建立QMutexLocker对象,并把mutex指针传给QMutexLocker对象,此时mutex已经加锁,等到退出函数后,QMutexLocker对象局部变量会自己销毁,此时mutex解锁。
    头文件声明:    #include<QMutexLocker>
    互斥锁声明:    QMutexLocker mutexLocker(&m_Mutex);
    互斥锁加锁:    从声明处开始(在构造函数中加锁)
    互斥锁解锁:    出了作用域自动解锁(在析构函数中解锁)

     int complexFunction(int flag) QMutexLocker locker(&mutex); if (status < 0) if (flag > 10) 

    4、QReadWriteLock

     QReadWriteLock ( ) QReadWriteLock ( RecursionMode recursionMode ) void lockForRead () void lockForWrite () bool tryLockForRead () bool tryLockForRead ( int timeout ) bool tryLockForWrite () bool tryLockForWrite ( int timeout ) boid unlock () 使用实例: QReadWriteLock lock; void ReaderThread::run() lock.lockForRead(); read_file(); lock.unlock(); void WriterThread::run() lock.lockForWrite(); write_file(); 

    5、QReadLocker和QWriteLocker

        在较复杂的函数和异常处理中对QReadWriterLock类lock对象进行lockForRead()/lockForWrite()和unlock()操作将会很复杂,进入点要lockForRead()/lockForWrite(),在所有跳出点都要unlock(),很容易出现在某些跳出点未调用unlock(),所以Qt引进了QReadLocker和QWriteLocker类来简化解锁操作。在函数需要的地方建立QReadLocker或QWriteLocker对象,并把lock指针传给QReadLocker或QWriteLocker对象,此时lock已经加锁,等到退出函数后,QReadLocker或QWriteLocker对象局部变量会自己销毁,此时lock解锁。

     QByteArray readData() ... return data; 

    使用QReadLocker:

     QReadLocker locker(&lock); 

    6、信号量QSemaphore

     QSemaphore ( int n = 0 ) void acquire ( int n = 1 ) int available () const void release ( int n = 1 ) bool tryAcquire ( int n = 1 ) bool tryAcquire ( int n, int timeout ) 

     QSemaphore sem(5); // sem.available() == 5 sem.acquire(3); // sem.available() == 2 sem.acquire(2); // sem.available() == 0 sem.release(5); // sem.available() == 5 sem.release(5); // sem.available() == 10 sem.tryAcquire(1); // sem.available() == 9, returns true sem.tryAcquire(250); // sem.available() == 9, returns false 

     #include <QtCore/QCoreApplication> #include <QSemaphore> #include <cstdlib> #include <cstdio> const int DataSize = ; const int BufferSize = 8192; char buffer[BufferSize]; QSemaphore production(BufferSize); QSemaphore consumption; class Producor:public QThread void run(); void Producor::run() for(int i = 0; i < DataSize; i++) production.acquire(); buffer[i%BufferSize] = "ACGT"[(int)qrand()%4]; consumption.release(); } class Consumer:public QThread void Consumer::run() consumption.acquire(); fprintf(stderr, "%c", buffer[i%BufferSize]); production.release(); fprintf(stderr, "%c", "\n"); int main(int argc, char *argv[]) QCoreApplication a(argc, argv); Producor productor; Consumer consumer; productor.start(); consumer.start(); productor.wait(); consumer.wait(); return a.exec(); 

    Producer::run函数:
       当producer线程执行run函数,如果buffer中已满,而consumer线程没有读,producer不能再往buffer中写字符,在 productor.acquire 处阻塞直到 consumer线程读(consume)数据。一旦producer获取到一个字节(资源)就写入一个随机的字符,并调用 consumer.release 使consumer线程可以获取一个资源(读一个字节的数据)。

    Consumer::run函数:
       当consumer线程执行run函数,如果buffer中没有数据,则consumer线程在consumer.acquire处阻塞,直到producer线程执行写操作写入一个字节,并执行consumer.release 使consumer线程的可用资源数=1时,consumer线程从阻塞状态中退出, 并将consumer 资源数-1,consumer当前资源数=0。

    7、等待条件QWaitCondition

     QWaitCondition () bool wait ( QMutex * mutex, unsigned long time = ULONG_MAX ) bool wait ( QReadWriteLock * readWriteLock, unsigned long time = ULONG_MAX ) void wakeOne () void wakeAll () 

    头文件声明:    #include <QWaitCondition>
    等待条件声明:    QWaitCondtion m_WaitCondition;
    等待条件等待:    m_WaitConditon.wait(&m_muxtex, time);
    等待条件唤醒:    m_WaitCondition.wakeAll();

     #include <QWaitCondition> #include <QMutex> #include <QTime> const int DataSize = 32; const int BufferSize = 16; QWaitCondition bufferNotEmpty; QWaitCondition bufferNotFull; int used = 0; qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); mutex.lock(); if(used == BufferSize) bufferNotFull.wait(&mutex); mutex.unlock(); buffer[i%BufferSize] = used; used++; bufferNotEmpty.wakeAll(); if(used == 0) bufferNotEmpty.wait(&mutex); fprintf(stderr, "%d\n", buffer[i%BufferSize]); used--; bufferNotFull.wakeAll(); 

    8、高级事件队列

    五、可重入与线程安全

    可重入reentrant与线程安全thread-safe被用来说明一个函数如何用于多线程程序。
    一个线程安全的函数可以同时被多个线程调用,甚至调用者会使用共享数据也没有问题,因为对共享数据的访问是串行的。一个可重入函数也可以同时被多个线程调用,但是每个调用者只能使用自己的数据。因此,一个线程安全的函数总是可重入的,但一个可重入的函数并不一定是线程安全的。
        一个可重入的类,指的是类的成员函数可以被多个线程安全地调用,只要每个线程使用类的不同的对象。而一个线程安全的类,指的是类的成员函数能够被多线程安全地调用,即使所有的线程都使用类的同一个实例。

    1、可重入

     class Counter public: Counter() {n=0;} void increment() {++n;} void decrement() {--n;} int value() const {return n;} private: int n; 

        Counter类是可重入的,但却不是线程安全的。假如多个线程都试图修改数据成员n,结果未定义。
        大多数Qt类是可重入,非线程安全的。有一些类与函数是线程安全的,主要是线程相关的类,如QMutex,QCoreApplication::postEvent()。

    2、线程安全

     public: Counter() { n = 0; } void increment() { QMutexLocker locker(&mutex); ++n; } void decrement() { QMutexLocker locker(&mutex); --n; } int value() const { QMutexLocker locker(&mutex); return n; } private: mutable QMutex mutex; int n; }; 

    六、线程与信号槽

    1、线程的依附性

        线程的依附性是对象与线程的关系。默认情况下,对象依附于自身被创建的线程。
        对象的依附性与槽函数执行的关系,默认情况下,槽函数在其所依附的线程中被调用执行。
        修改对象的依附性的方法:QObject::moveToThread函数用于改变对象的线程依附性,使得对象的槽函数在依附的线程中被调用执行。

    2、QObject与线程

    QThread类具有发送信号和定义槽函数的能力。QThread主要信号如下:

    • void started();线程开始运行时发送信号
    • void finished();线程完成运行时发送信号
    • void terminated();线程被异常终止时发送信号

        A、一个QObject类型的孩子必须总是被创建在它的父亲所被创建的线程中。这意味着,除了别的以外,永远不要把QThread对象(this)作为该线程中创建的一个对象的父亲(因为QThread对象自身被创建在另外一个线程中)。
        B、事件驱动的对象可能只能被用在一个单线程中。特别适用于计时器机制(timer mechanism)和网络模块。例如:不能在不属于这个对象的线程中启动一个定时器或连接一个socket,必须保证在删除QThread之前删除所有创建在这个线程中的对象。在run()函数的实现中,通过在栈中创建这些对象,可以轻松地做到这一点。
        C、虽然QObject是可重入的,但GUI类,尤其是QWidget及其所有子类都不是可重入的,只能被用在GUI线程中。QCoreApplication::exec()必须也从GUI线程被调用。

    3、线程的事件循环

    4、其他线程访问QObject子类

        QObject及其所有子类都不是线程安全的。这包含了整个事件交付系统。重要的是,切记事件循环可能正在向你的QObject子类发送事件,当你从另一个线程访问该对象时。
        如果你正在调用一个QObject子类的函数,而该子类对象并不存活于当前线程中,并且该对象是可以接收事件的,那么你必须用一个mutex保护对该QObject子类的内部数据的所有访问,否则,就有可能发生崩溃和非预期的行为。
        同其它对象一样,QThread对象存活于该对象被创建的线程中 – 而并非是在QThread::run()被调用时所在的线程。一般来说,在QThread子类中提供槽函数是不安全的,除非用一个mutex保护成员变量。
        另一方面,可以在QThread::run()的实现中安全地发射信号,因为信号发射是线程安全的。

    5、跨线程的信号槽

        线程的信号槽机制需要开启线程的事件循环机制,即调用QThread::exec()函数开启线程的事件循环。
    Qt信号-槽连接函数原型如下:

     bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection ) 

    A、Qt::DirectConnection(直连方式)(信号与槽函数关系类似于函数调用,同步执行)
        当信号发出后,相应的槽函数将立即被调用。emit语句后的代码将在所有槽函数执行完毕后被执行。
        当信号发射时,槽函数将直接被调用。
        无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行。
    B、Qt::QueuedConnection(队列方式)(此时信号被塞到事件队列里,信号与槽函数关系类似于消息通信,异步执行)
        当信号发出后,排队到信号队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,调用相应的槽函数。emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕。
        当控制权回到接收者所依附线程的事件循环时,槽函数被调用。
        槽函数在接收者所依附线程执行。
    C、Qt::AutoConnection(自动方式)
         Qt的默认连接方式,如果信号的发出和接收信号的对象同属一个线程,那个工作方式与直连方式相同;否则工作方式与队列方式相同。
    如果信号在接收者所依附的线程内发射,则等同于直接连接
    如果发射信号的线程和接受者所依附的线程不同,则等同于队列连接
    D、Qt::BlockingQueuedConnection(信号和槽必须在不同的线程中,否则就产生死锁)
        槽函数的调用情形和Queued Connection相同,不同的是当前的线程会阻塞住,直到槽函数返回。
    E、Qt::UniqueConnection
        与默认工作方式相同,只是不能重复连接相同的信号和槽,因为如果重复连接就会导致一个信号发出,对应槽函数就会执行多次。

        QThread是用来管理线程的,QThread对象所依附的线程和所管理的线程并不是同一个概念。QThread所依附的线程,就是创建QThread对象的线程,QThread 所管理的线程,就是run启动的线程,也就是新建线程。QThread对象依附在主线程中,QThread对象的slot函数会在主线程中执行,而不是次线程。除非QThread对象依附到次线程中(通过movetoThread)。
    工程实践中,为了避免冻结主线程的事件循环(即避免因此而冻结了应用的UI),所有的计算工作是在一个单独的工作线程中完成的,工作线程结束时发射一个信号,通过信号的参数将工作线程的状态发送到GUI线程的槽函数中更新GUI组件状态。

    七、线程的设计

    1、线程的生命周期

    如果线程的正处于执行过程中时,线程对象被销毁时,程序将会出错。
    工程实践中线程对象的生命期必须大于线程的生命期。

    2、同步线程类设计

    线程对象主动等待线程生命期结束后才销毁,线程对象销毁时确保线程执行结束,支持在栈或堆上创建线程对象。
    在线程类的析构函数中先调用wait函数,强制等待线程执行结束。
    使用场合:适用于线程生命期较短的场合

     #ifndef SYNCTHREAD_H #define SYNCTHREAD_H class SyncThread : public QThread Q_OBJECT explicit SyncThread(QObject* parent = 0):QThread(parent) ~SyncThread() wait(); #endif // SYNCTHREAD_H 

    3、异步线程类设计

    线程生命期结束时通知线程对象销毁。
    只能在堆空间创建线程对象,线程对象不能被外界主动销毁。
    在run函数中最后调用deleteLater()函数。
    线程函数主动申请销毁线程对象。
    使用场合:
    线程生命期不可控,需要长时间运行于后台的线程。

     #ifndef ASYNCTHREAD_H #define ASYNCTHREAD_H class AsyncThread : public QThread deleteLater(); explicit AsyncThread(QObject* parent = 0):QThread(parent) ~AsyncThread() static AsyncThread* newThread(QObject* parent = 0) return new AsyncThread(parent); #endif // ASYNCTHREAD_H 

    八、线程的使用方式

    1、子类化QThread

    QThread的两种使用方法:
    (1)不使用事件循环
    A、子类化 QThread
        B、重写run函数,run函数内有一个 while 或 for 的死循环
        C、设置一个标记为来控制死循环的退出。
        适用于后台执行长时间的耗时操作,如文件复制、网络数据读取。
    (2)使用事件循环。
        A、子类化 QThread
        B、重写run 使其调用 QThread::exec() ,开启线程的事件循环
    C、为子类定义信号和槽,由于槽函数并不会在新开的 Thread 运行,在构造函数中调用 moveToThread(this)。
    适用于事务性操作,如文件读写、数据库读写。

    2、Worker-Object

        在Qt4.4之前,run 是纯虚函数,必须子类化QThread来实现run函数。
        而从Qt4.4开始,QThread不再支持抽象类,run 默认调用 QThread::exec() ,不需要子类化 QThread,只需要子类化一个 QObject 。
        通过继承的方式实现多线程已经没有任何意义,QThread是操作系统线程的接口或控制点,用于充当线程操作的集合。
        使用Worker-Object通过QObject::moveToThread将它们移动到线程中。
        指定一个线程对象的线程入口函数的方法:
    A、在类中定义一个槽函数void tmain()作为线程入口函数
    B、在类中定义一个QThread成员对象m_thread
    C、改变当前对象的线程依附性到m_thread
    D、连接m_thread的started()信号到tmain槽函数。

     #ifndef WORKER_H #define WORKER_H #include <QObject> class Worker : public QObject QThread m_thread; protected slots: void tmain() qDebug() << "void tmain()"; explicit Worker(QObject* parent = 0):QObject(parent) moveToThread(&m_thread); connect(&m_thread, SIGNAL(started()), this, SLOT(tmain())); void start() m_thread.start(); void terminate() m_thread.terminate(); void exit(int c) m_thread.exit(c); ~Worker() m_thread.wait(); #endif // WORKER_H 

    九、多线程与GUI组件的通信

    1、多线程与GUI组件通信基础

        GUI系统的设计原则:
        所有界面组件的创建只能在GUI线程(主线程)中完成。子线程与界面组件的通信有两种方式:
        A、信号槽方式
        B、发送自定事件方式

    2、信号槽方式

    使用信号槽解决多线程与界面组件的通信的方案:
    A、在子线程中定义界面组件的更新信号
    B、在主窗口类中定义更新界面组件的槽函数
    C、使用异步方式连接更新信号到槽函数
    子线程通过发送信号的方式更新界面组件,所有的界面组件对象只能依附于GUI线程(主线程)。
    子线程更新界面状态的本质是子线程发送信号通知主线程界面更新请求,主线程根据具体信号以及信号参数对界面组件进行修改。
    使用信号槽在子线程中更新主界面中进度条的进度显示信息。
    工作线程类:

     signals: void signalProgressValue(int value); work(); exec(); moveToThread(this); void work() for(int i = 0; i < 11; i++) emit signalProgressValue(i*10); sleep(1); 

     #ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QProgressBar> #include "WorkThread.h" class Widget : public QWidget QProgressBar* m_progress;//进度条 WorkThread* m_thread;//工作线程 Widget(QWidget *parent = 0):QWidget(parent) m_progress = new QProgressBar(this); m_progress->move(10, 10); m_progress->setMinimum(0); m_progress->setMaximum(100); m_progress->setTextVisible(true); m_progress->resize(100, 30); m_thread = new WorkThread(); m_thread->start(); connect(m_thread, SIGNAL(finished()), m_thread, SLOT(deleteLater())); //连接工作线程的信号到界面的槽函数 connect(m_thread, SIGNAL(signalProgressValue(int)), this, SLOT(onProgress(int))); ~Widget() void onProgress(int value) m_progress->setValue(value); #endif // WIDGET_H 

     #include "Widget.h" #include <QApplication> QApplication a(argc, argv); Widget w; w.show(); return a.exec(); 

    3、发送自定义事件方式

        A、自定义事件用于描述界面更新细节
        B、在主窗口类中重写事件处理函数event
        C、使用postEvent函数(异步方式)发送自定义事件类对象
        子线程指定接收消息的对象为主窗口对象,在event事件处理函数更新界面状态
        事件对象在主线程中被处理,event函数在主线程中调用。
        发送的事件对象必须在堆空间创建
        子线程创建时必须附带目标对象的地址信息
    自定义事件类:

     #ifndef PROGRESSEVENT_H #define PROGRESSEVENT_H #include <QEvent> class ProgressEvent : public QEvent int m_progress; const static Type TYPE = static_cast<Type>(QEvent::User + 0xFF); ProgressEvent(int progress = 0):QEvent(TYPE) m_progress = progress; int progress()const return m_progress; #endif // PROGRESSEVENT_H 

     #include <ProgressEvent.h> QApplication::postEvent(parent(), new ProgressEvent(i*10)); 

     #ifndef WIDGETUI_H #define WIDGETUI_H #include "ProgressEvent.h" class WidgetUI : public QWidget WidgetUI(QWidget *parent = 0):QWidget(parent) m_thread->setParent(this); ~WidgetUI() m_thread->quit(); bool event(QEvent *event) bool ret = true; if(event->type() == ProgressEvent::TYPE) ProgressEvent* evt = dynamic_cast<ProgressEvent*>(event); if(evt != NULL) //设置进度条的进度为事件参数的值 m_progress->setValue(evt->progress()); else ret = QWidget::event(event); return ret; #endif // WIDGETUI_H #include "WidgetUI.h" WidgetUI w;

    到此这篇关于深入理解QT多线程编程 的文章就介绍到这了,更多相关QT多线程编程 内容请搜索本网站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本网站!

    您可能感兴趣的文章:

    • Qt5.9实现简单的多线程实例(类QThread)
    • QT实现多线程两种方式案例详解
    • Qt5.9继承QObject创建多线程实例