Qt⾼级编程技巧(⼆)--编写多线程和并发应⽤
学习QT多线程编程之前,有必要先熟悉事件循环的概念。先看⼀个单线程界⾯程序的主函数代码:
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
// 构造主窗⼝对象并显⽰
MainWindow w;
w.show();
// 进⼊事件循环
();
}
在程序初始化完成后,主线程进⼊main()函数开始执⾏应⽤代码。⼀般地,我们在主线程上构建界⾯对象,然后进⼊事件循环以处理控件绘制、⽤户输⼊、系统输出等消息。这就是我们通常说的事件驱动模型。小暑节气
主线程承担着⽤户交互的重任,当在主线程上运⾏费时的代码时,就会影响⽤户的正常操作。所以我们常把⼀些费时费⼒的计算⼯作移出主线程,开辟新的线程来运⾏之。
QThread是QT中⽤于线程管理的类,调⽤⼀个QThread对象的start()⽅法时,会创建⼀个新的线程并执⾏它的run()⽅法。默认地,run()会调⽤exec()⽅法进⼊⾃⼰的消息循环中。如下图所⽰:
上图中有主线程、⼯作线程都是执⾏事件循环,并且注意到主线程内部含有thr、w、objs这些QObject
对象(这些对象都是在主线程上创建的)。主线程的事件循环负责检测这些对象是否有消息要处理,有的话则调⽤对象的slot⽅法。可以使⽤QObject::moveToThread⽅法将某个对象移到其他线程中,譬如:
class Worker : public QObject {
Q_OBJECT
…
}
void someFunc()
{
遇见的作文
QThread thr = new QThread;
Worker worker = new Worker;
worker->moveToThread(thr);
流媒体是什么thr->start();
…
}
如果在主线程上调⽤someFunc(),则workerThread和worker在创建后都是关联在主线程上,当调⽤worker->moveToThread()后,worker对象关联到了新的线程中,如图所⽰:
假定我们在MainWindow上声明了⼀个worksSignal()消息,在Worker对象上声明和定义了handleWorks()的槽,将worksSignal和handleWorks连接起来的⽅式有:
1. Qt::AutoConnection - (默认)如果消息对象和槽对象关联在同⼀线程下,则使⽤Qt::DirectConnection⽅式;否则的话,像MainWindow
和Worker两个关联在不同线程的对象,将采⽤Qt::QueuedConnection的⽅式。
2. QT::DirectConnection - 发送消息的时候将直接调⽤槽对象的槽⽅法。注意这⾥的槽⽅法是在发送消息的线程上执⾏的,如果该槽⽅法是⾮线程安全的话会有问题的。
3. Qt::QueuedConnection - 发送线程在发送消息后将继续执⾏,槽对象关联的线程在事件循环时会检测到该消息,并调⽤相应的槽⽅法。
史记好词好句4. Qt::BlockingQueuedConnection - 在主线程发送worksSignal消息后,将阻塞直到在⼯作线程检测到该消息并运⾏worker->handleWorks()后恢复。
5. Qt::UniqueConnection - 可以和上⾯4个⽅式联并(或操作),提⽰该连接是独⼀的。提⽰不能有相同的连接(消息对象和槽对象,消息和槽都相同)出现。
这⾥特别提醒读者,⼀般地我们不建议将QThread对象moveToThread到它运⾏的线程上。原因是QThread是设计成⼀个管理线程的类,我们不应该在⼯作线程上管理⼯作线程,对吧。关于更多的技术细节,我不想多讲了,因为本系列的博⽂旨在共享经验技巧,⽽⾮翻译⼀些⽂档。
在项⽬中,我都是通过继承QThread类实现后台进程的,通过重写run()函数填⼊线程需要运⾏的任务。上⼀篇博⽂中,我通过在QThread⼦类上嵌⼊InThreadObject对象快速实现线程通信的功能,请
回顾。还有下⾯⼀个技巧实现⼯作线程上的timer事件处理:
void WorkerThread::run()
{
QTimer tmr; // 关联在本线程上的QObject对象
connect(&tmr, &QTimer::timeout, [=](){
大班社会活动教案 doSomething();
});
调价函 tmr.start(500); // 500毫秒计时
exec(); // 进⼊事件循环
}健美操的特点
标题大全关于多线线程的编程谈论到此,望能起抛砖引⽟的效果。在实际的项⽬,可以参考下⾯的⽂档设计多线程或并发的应⽤: