• Qt之QThread


    简述

    QThread类提供了与系统无关的线程。

    QThread代表在程序中一个单独的线程控制。线程在run()中开始执行,默认情况下,run()通过调用exec()启动事件循环并在线程里运行一个Qt的事件循环。

    详细描述

    当线程started()和finished()时,QThread会通过一个信号通知你,可以使用isFinished()和isRunning()来查询线程的状态。

    你可以通过调用exit()或quit()来停止线程。在极端情况下,可能要强行terminate()一个执行线程。但是,这样做很危险,下面会详细说明。

    从Qt4.8起,可以释放运行刚刚结束的线程对象,通过连接finished()信号到QObject::deleteLater()槽。
    使用wait()来阻塞调用的线程,直到其它线程执行完毕(或者直到指定的时间过去)。

    QThread还提供了静态的、平台独立的休眠函数:sleep()、msleep()、usleep(),允许秒,毫秒和微秒来区分,这些函数在Qt5.0中被设为public。

    注意:一般情况下,wait()和sleep()函数应该不需要,因为Qt是一个事件驱动型框架。考虑监听finished()信号来取代wait(),使用QTimer来取代sleep()。

    静态函数currentThreadId()和currentThread()返回标识当前正在执行的线程。前者返回该线程平台特定的ID,后者返回一个线程指针。

    要设置线程的名称,可以在启动线程之前调用setObjectName()。如果不调用setObjectName(),线程的名称将是线程对象的运行时类型(QThread子类的类名)。

    线程管理

    可以将常用的接口按照功能进行以下分类:

    • 线程启动

      • void start(Priority priority = InheritPriority) [slot]
        调用后会执行run()函数,但在run()函数执行前会发射信号started(),操作系统将根据优先级参数调度线程。如果线程已经在运行,那么这个函数什么也不做。优先级参数的效果取决于操作系统的调度策略。特别是那些不支持线程优先级的系统优先级将会被忽略(例如在Linux中,更多细节请参考http://linux.die.net/man/2/sched_setscheduler)。
    • 线程执行

      • int exec() [protected]
        进入事件循环并等待直到调用exit(),返回值是通过调用exit()来获得,如果调用成功则范围0。

      • void run() [virtual protected]
        线程的起点,在调用start()之后,新创建的线程就会调用这个函数,默认实现调用exec(),大多数需要重新实现这个函数,便于管理自己的线程。该方法返回时,该线程的执行将结束。

    • 线程退出

      • void quit() [slot]
        告诉线程事件循环退出,返回0表示成功,相当于调用了QThread::exit(0)。

      • void exit(int returnCode = 0)
        告诉线程事件循环退出。
        调用这个函数后,线程离开事件循环后返回,QEventLoop::exec()返回returnCode,按照惯例,0表示成功;任何非0值表示失败。

      • void terminate() [slot]
        终止线程,线程可能会立即被终止也可能不会,这取决于操作系统的调度策略,使用terminate()之后再使用QThread::wait(),以确保万无一失。当线程被终止后,所有等待中的线程将会被唤醒。
        警告:此函数比较危险,不鼓励使用。线程可以在代码执行的任何点被终止。线程可能在更新数据时被终止,从而没有机会来清理自己,解锁等等。。。总之,只有在绝对必要时使用此函数。

      • void requestInterruption()
        请求线程的中断。该请求是咨询意见并且取决于线程上运行的代码,来决定是否及如何执行这样的请求。此函数不停止线程上运行的任何事件循环,并且在任何情况下都不会终止它。

    • 线程等待

      • void msleep(unsigned long msecs) [static]
        强制当前线程睡眠msecs毫秒

      • void sleep(unsigned long secs) [static]
        强制当前线程睡眠secs秒

      • void usleep(unsigned long usecs) [static]
        强制当前线程睡眠usecs微秒

      • bool wait(unsigned long time = ULONG_MAX)
        线程将会被阻塞,等待time毫秒。和sleep不同的是,如果线程退出,wait会返回。

    • 线程状态

      • bool isFinished() const
        线程是否结束

      • bool isRunning() const
        线程是否正在运行

    • bool isInterruptionRequested() const
      如果线程上的任务运行应该停止,返回true。可以使用requestInterruption()请求中断。
      此函数可用于使长时间运行的任务干净地中断。从不检查或作用于该函数返回值是安全的,但是建议在长时间运行的函数中经常这样做。注意:不要过于频繁调用,以保持较低的开销。

    void long_task() {
         forever {
            if ( QThread::currentThread()->isInterruptionRequested() ) {
                return;
            }
        }
    }
    • 线程优先级

      • void setPriority(Priority priority)
        设置正在运行线程的优先级。如果线程没有运行,此函数不执行任何操作并立即返回。使用的start()来启动一个线程具有特定的优先级。优先级参数可以是QThread::Priority枚举除InheritPriortyd的任何值。

    枚举QThread::Priority:

    常量 描述
    QThread::IdlePriority 0 没有其它线程运行时才调度
    QThread::LowestPriority 1 比LowPriority调度频率低
    QThread::LowPriority 2 比NormalPriority调度频率低
    QThread::NormalPriority 3 操作系统的默认优先级
    QThread::HighPriority 4 比NormalPriority调度频繁
    QThread::HighestPriority 5 比HighPriority调度频繁
    QThread::TimeCriticalPriority 6 尽可能频繁的调度
    QThread::InheritPriority 7 使用和创建线程同样的优先级. 这是默认值

    使用方式

    worker-object

    可以使用worker-object通过QObject::moveToThread将它们移动到线程中。

    class Worker : public QObject
    {
        Q_OBJECT
    
    public slots:
        void doWork(const QString &parameter) {
            QString result;
            // 这里是昂贵的或阻塞的操作
            emit resultReady(result);
        }
    
    signals:
        void resultReady(const QString &result);
    };
    
    class Controller : public QObject
    {
        Q_OBJECT
        QThread workerThread;
    public:
        Controller() {
            Worker *worker = new Worker;
            worker->moveToThread(&workerThread);
            connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
            connect(this, &Controller::operate, worker, &Worker::doWork);
            connect(worker, &Worker::resultReady, this, &Controller::handleResults);
            workerThread.start();
        }
        ~Controller() {
            workerThread.quit();
            workerThread.wait();
        }
    public slots:
        void handleResults(const QString &);
    signals:
        void operate(const QString &);
    };

    Worker槽中的代码将在一个单独的线程中执行,然而,可以自由地将Worker的槽函数连接到任何信号(来自任何对象、在任何线程中),在不同的线程里连接信号和槽也是安全的,这要归功于一个名为Qt::QueuedConnection的连接机制。

    子类化QThread

    另一种使代码运行在一个单独的线程中的方法,是子类化QThread中并重新实现的run()函数。

    class WorkerThread : public QThread
    {
        Q_OBJECT
        void run() Q_DECL_OVERRIDE {
            QString result;
            // 这里是昂贵的或阻塞的操作
            emit resultReady(result);
        }
    signals:
        void resultReady(const QString &s);
    };
    
    void MyObject::startWorkInAThread()
    {
        WorkerThread *workerThread = new WorkerThread(this);
        connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
        connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
        workerThread->start();
    }

    上面的示例,在run()函数返回后线程就会退出,在线程中将不会有任何的事件循环运行,除非调用exec()。

    重要的是要记住,一个线程实例位于实例化它的旧线程中,而非调用run()的新线程中,这意味着所有线程的queued slots将在旧线程中执行。因此,如果要在新线程调用槽函数必须使用worker-object方法,新的槽函数不应直接在子类化QThread中来实现。

    当子类化QThread时,谨记构造函数在旧线程中执行,然而run()在新线程中执行。如果一个成员变量的访问来自两个函数,然后从两个不同的线程访问变量,需要检查这样做是否安全。

    注意:用在不同的线程中的对象进行交互时必须小心。详见同步线程(Synchronizing Threads)。

    耗时操作

    • requestInterruption()
    • isInterruptionRequested()

    请参考:Qt之模型/视图(自定义进度条)

  • 相关阅读:
    Spring代理模式
    Spring注解的步骤
    Spring与Struts2集成开发
    Spring框架之控制反转和依赖注入
    使用Spring框架的步骤
    Hibernate锁机制
    Hibernate的缓存机制
    Hibernate框架之HQL查询与Criteria 查询的区别
    Hibernate框架之Criteria 详解
    Ajax异步刷新省市级联
  • 原文地址:https://www.cnblogs.com/itrena/p/5938295.html
Copyright © 2020-2023  润新知