GUI线程不应该执行长时间的程序,以免界面卡死无法响应。耗时较长的程序可以在其他线程执行,并与GUI线程交互。Qt中界面应该只出现在主线程中,其他任务则可以放到子线程。
1. 我的方法
我在项目中使用QObject::moveToThread这种方式实现多线程,将多线程与应用逻辑区分开,无需继承QThread类,无需改写QThread::run方法,现有的逻辑代码可以很方便地使用进来(仅需继承QObject类,加入一些信号与槽)。项目中我参照网上的一篇博文:
1 2 3 4 5 6 7 8 9 10 11 12 |
void TaskManager::runTask() { SshTask* a_task = new SshTask(task_config_); a_task->moveToThread(&work_thread_); connect(&work_thread_, SIGNAL(finished()), a_task, SLOT(deleteLater())); connect(&work_thread_, SIGNAL(terminated()), a_task, SLOT(deleteLater())); connect(&work_thread_,SIGNAL(started()),a_task,SLOT(runTask())); work_thread_.start(); return; } |
从文章开头提到的文档中可知,QObject::moveToThread方法中的QThread是持续运行的,任务对象执行完成时所在的工作线程不会停止。我将工作线程的start信号与任务相联系,仅在工作线程启动时执行程序。当再一次执行runTask方法时,工作进程没有停止,就无法用start信号执行下一次任务。对此,我在任务SshTask执行结束时终止线程运行,需要更多的信号与槽。修改版如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
void TaskManager::runTask() { if(work_thread_.isRunning()) { QMessageBox(QMessageBox::Critical,tr("Run Task Failed"),tr("Please wait for the current task to finish")).exec(); return; } //...与上面相同... // 验证 connect(a_task,SIGNAL(taskCompleteSuccessfulSignal(QString)),this,SLOT(verifySubmit(QString))); connect(a_task,SIGNAL(taskRunFailedSignal(TaskMessage)),this,SLOT(taskRunFailedSlot(TaskMessage))); qDebug()<<"TaskManager: start the task"; work_thread_.start(); return; } void TaskManager::verifySubmit(const QString &task_std_out) { // ... work_thread_.quit(); work_thread_.wait(); return; } void TaskManager::taskRunFailedSlot(const TaskMessage &task_message) { //... work_thread_.quit(); work_thread_.wait(); return; } |
只允许一个任务运行,首先判断线程是否在运行,任务结束时强制终止线程。方法很繁琐,代码不优雅。
2. QThread文档中推荐的方法
Qt文档中对线程的详细介绍:《Threading Basics》。文中就是否使用多线程,以及何种方式实现多线程给出详细的介绍。
QThread文档中有此种方法的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class Worker : public QObject { Q_OBJECT QThread workerThread; public slots: void doWork(const QString ¶meter) { // ... 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, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString))); connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString))); workerThread.start(); } ~Controller() { workerThread.quit(); workerThread.wait(); } public slots: void handleResults(const QString &); signals: void operate(const QString &); }; |
QThread文档中给出的方法就没有上面的问题,任务Worker运行不受工作线程workerThread控制,由管理对象Controller的operate信号开启,这样就可以重复多次调用Worker任务。子线程在Controller生命周期内一直运行,但我还没用到项目中,不知道怎样才能保证只有一个任务运行,最近试一下能否同时进行多个任务。