QThread的使用一点经验
2017-12-29 17:07
316 查看
本文结合实际使用,初略的整理了一下Qt中使用Qthread进行多线程的编程方法。QThread实现方法有两种:
继承与QThread并重写run函数;
使用QObject::moveToThread函数;
举个简单的例子:
- 方法一:通过设置一个bool变量控制run()函数中的while循环,注意对bool变量的互锁;
- 方法二:通过isInterruptionRequested()和requestInterruption()控制run()函数中的while循环,内部以实现互锁;
注意:链接到子线程中执行的槽函数会根据触发及链接的先后顺序,依次执行,如
Qt::DirectConnection
Qt::QueuedConnection
Qt::BlockingQueuedConnection
Qt::UniqueConnection
Qt::AutoCompatConnection
前两种比较相似,都是同一线程之间连接的方式,不同的是Qt::AutoConnection是系统默认的连接方式。这种方式连接的时候,槽不是马上被执行的,而是进入一个消息队列,待到何时执行就不是我们可以知道的了,当信号和槽不是同个线程,会使用第三种QT::QueueConnection的链接方式。如果信号和槽是同个线程,调用第二种Qt::DirectConnection链接方式。
第二种Qt::DirectConnection是直接连接,也就是只要信号发出直接就到槽去执行,无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行,一旦使用这种连接,槽将会不在线程执行!。
第三种Qt::QueuedConnection和第四种Qt::BlockingQueuedConnection是相似的,都是可以在不同进程之间进行连接的,不同的是,这里第三种是在对象的当前线程中执行,并且是按照队列顺序执行。当当前线程停止,就会等待下一次启动线程时再按队列顺序执行 ,等待QApplication::exec()或者线程的QThread::exec()才执行相应的槽,就是说:当控制权回到接受者所依附线程的事件循环时,槽函数被调用,而且槽函数在接收者所依附线程执行,使用这种连接,槽会在线程执行。
第四种Qt::BlockingQueuedConnection是(必须信号和曹在不同线程中,否则直接产生死锁)这个是完全同步队列只有槽线程执行完才会返回,否则发送线程也会等待,相当于是不同的线程可以同步起来执行。
第五种Qt::UniqueConnection跟默认工作方式相同,只是不能重复连接相同的信号和槽;因为如果重复链接就会导致一个信号发出,对应槽函数就会执行多次。
第六种Qt::AutoCompatConnection是为了连接QT4 到QT3的信号槽机制兼容方式,工作方式跟Qt::AutoConnection一样。显然这里我们应该选择第三种方式,我们不希望子线程没结束主线程还要等,我们只是希望利用这个空闲时间去干别的事情,当子线程执行完了,只要发消息给主线程就行了,到时候主线程会去响应。
moveToThread对比传统子类化Qthread更灵活,仅需要把你想要执行的代码放到槽,movetothread这个object到线程,然后拿一个信号连接到这个槽就可以让这个槽函数在线程里执行。可以说,movetothread给我们编写代码提供了新的思路,当然不是说子类化qthread不好,只是你应该知道还有这种方式去调用线程。轻量级的函数可以用movethread,多个短小精悍能返回快速的线程函数适用 ,无需创建独立线程类,例如你有20个小函数要在线程内做, 全部扔给一个QThread。而我觉得movetothread和子类化QThread的区别不大,更可能是使用习惯引导。又或者你一开始没使用线程,但是后边发觉这些代码还是放线程比较好,如果用子类化QThread的方法重新设计代码,将会有可能让你把这一段推到重来,这个时候,moveThread的好处就来了,你可以把这段代码的从属着movetothread,把代码移到槽函数,用信号触发它就行了。其它的话movetothread它的效果和子类化QThread的效果是一样的,槽就相当于你的run()函数,你往run()里塞什么代码,就可以往槽里塞什么代码,子类化QThread的线程只可以有一个入口就是run(),而movetothread就有很多触发的入口。
继承与QThread并重写run函数;
使用QObject::moveToThread函数;
线程的创建及执行
继承QThread使用方法
创建一个继承于QThread的自定义类,实现其run()函数,在run函数中的都是在新线程中调用,自定义类中的其他函数都是在主线程中执行。在主线程中直接调用start函数,启动子线程执行。例如class WorkerThread : public QThread { Q_OBJECT void run() override { QString result; /* ... here is the expensive or blocking operation ... */ qDebug()<<"Current Thread Id: "<<QThread::currentThreadId(); 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(); //开启线程 }
QObject::moveToThread
创建一个继承于QObject的自定义类,然后通过QObject::moveToThread(QThread *)函数,把这个类移动到新的线程中,然后通过信号槽Qt::QueuedConnection或者Qt::BlockingQueuedConnection模式进行线程间通讯,信号触发的槽在新的线程中执行,新线程中的信号链接的主线程中槽则在主线程中执行。但是主线程中通过直接调用自定义类中函数或者使用Qt::DirectConnection链接的槽函数,还是在主线程中执行。链接模式的具体说明见附。举个简单的例子:
class Worker : public QObject { Q_OBJECT public slots: void doWork(const QString ¶meter) { QString result; /* ... here is the expensive or blocking operation ... */ qDebug()<<"Current Thread Id: "<<QThread::currentThreadId(); 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);//在新建线程中执行槽函数doWork() connect(worker, &Worker::resultReady, this, &Controller::handleResults); //在主线程中执行槽函数handleResults // connect(this, &Controller::operate, worker, &Worker::doWork, , Qt::DirectConnection);//采用Qt::DirectConnection模式链接则槽函数doWork()在主线程中执行; // worker->doWork();//直接调用,则函数doWork()在主线程中执行 workerThread.start(); } ~Controller() { workerThread.quit(); workerThread.wait(); } public slots: void handleResults(const QString &); signals: void operate(const QString &); };
线程的终止
继承QThread的子线程终止方法
run()函数执行完成,线程自动退出。- 方法一:通过设置一个bool变量控制run()函数中的while循环,注意对bool变量的互锁;
- 方法二:通过isInterruptionRequested()和requestInterruption()控制run()函数中的while循环,内部以实现互锁;
void run() override { while(isThreadActive) //或者while(isInterruptionRequested()) { //do something... } } //在run()函数外设置isThreadActive = false或者调用requestInterruption()函数来退出while循环,结束run()函数的运行,从而退出子线程
QObject::moveToThread
通过moveToThread()创建的子线程如要结束,则必须调用quit()函数来退出,但是调用quit()函数并不会立即结束子线程,而是在等待子线程中槽函数运行完成之后,子线程才会退出。槽函数中while循环的控制可类似于上面run()函数的控制。注意:链接到子线程中执行的槽函数会根据触发及链接的先后顺序,依次执行,如
connect(this, &Controller::operate, worker, &Worker::doWork); connect(this, &Controller::operate, worker, &Worker::doWork2); //则子线程会在执行完成doWork槽函数之后再去执行doWord2槽函数.
附:connect()的六中链接模式
Qt::AutoConnectionQt::DirectConnection
Qt::QueuedConnection
Qt::BlockingQueuedConnection
Qt::UniqueConnection
Qt::AutoCompatConnection
前两种比较相似,都是同一线程之间连接的方式,不同的是Qt::AutoConnection是系统默认的连接方式。这种方式连接的时候,槽不是马上被执行的,而是进入一个消息队列,待到何时执行就不是我们可以知道的了,当信号和槽不是同个线程,会使用第三种QT::QueueConnection的链接方式。如果信号和槽是同个线程,调用第二种Qt::DirectConnection链接方式。
第二种Qt::DirectConnection是直接连接,也就是只要信号发出直接就到槽去执行,无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行,一旦使用这种连接,槽将会不在线程执行!。
第三种Qt::QueuedConnection和第四种Qt::BlockingQueuedConnection是相似的,都是可以在不同进程之间进行连接的,不同的是,这里第三种是在对象的当前线程中执行,并且是按照队列顺序执行。当当前线程停止,就会等待下一次启动线程时再按队列顺序执行 ,等待QApplication::exec()或者线程的QThread::exec()才执行相应的槽,就是说:当控制权回到接受者所依附线程的事件循环时,槽函数被调用,而且槽函数在接收者所依附线程执行,使用这种连接,槽会在线程执行。
第四种Qt::BlockingQueuedConnection是(必须信号和曹在不同线程中,否则直接产生死锁)这个是完全同步队列只有槽线程执行完才会返回,否则发送线程也会等待,相当于是不同的线程可以同步起来执行。
第五种Qt::UniqueConnection跟默认工作方式相同,只是不能重复连接相同的信号和槽;因为如果重复链接就会导致一个信号发出,对应槽函数就会执行多次。
第六种Qt::AutoCompatConnection是为了连接QT4 到QT3的信号槽机制兼容方式,工作方式跟Qt::AutoConnection一样。显然这里我们应该选择第三种方式,我们不希望子线程没结束主线程还要等,我们只是希望利用这个空闲时间去干别的事情,当子线程执行完了,只要发消息给主线程就行了,到时候主线程会去响应。
后记
为什么要使用moveToTread()呢。moveToThread对比传统子类化Qthread更灵活,仅需要把你想要执行的代码放到槽,movetothread这个object到线程,然后拿一个信号连接到这个槽就可以让这个槽函数在线程里执行。可以说,movetothread给我们编写代码提供了新的思路,当然不是说子类化qthread不好,只是你应该知道还有这种方式去调用线程。轻量级的函数可以用movethread,多个短小精悍能返回快速的线程函数适用 ,无需创建独立线程类,例如你有20个小函数要在线程内做, 全部扔给一个QThread。而我觉得movetothread和子类化QThread的区别不大,更可能是使用习惯引导。又或者你一开始没使用线程,但是后边发觉这些代码还是放线程比较好,如果用子类化QThread的方法重新设计代码,将会有可能让你把这一段推到重来,这个时候,moveThread的好处就来了,你可以把这段代码的从属着movetothread,把代码移到槽函数,用信号触发它就行了。其它的话movetothread它的效果和子类化QThread的效果是一样的,槽就相当于你的run()函数,你往run()里塞什么代码,就可以往槽里塞什么代码,子类化QThread的线程只可以有一个入口就是run(),而movetothread就有很多触发的入口。
相关文章推荐
- VC6使用#pragma warning的一点经验
- openMP的一点使用经验
- VC中使用XML的一点经验
- QTP中Description对象使用的一点经验
- jsp开发网站使用cookie的一点经验
- openMP的一点使用经验
- openMP的一点使用经验
- 关于继电器使用的一点经验教训!
- 使用Unity开发项目的一点经验
- css使用的一点经验
- 使用 Oracle Sql plus的一点经验
- openMP的一点使用经验
- openMP的一点使用经验
- [转]openmp的一点使用经验
- Contains的使用,一点经验
- 长期使用中型Access数据库的一点经验与缺点
- PHP中SESSION使用中的一点经验总结
- asterisknow 安装使用的一点经验!
- 关于char以及varchar2在实际使用时的一点经验
- openMP的一点使用经验