(原链接地址)http://windrocblog.sinaapp.com/?p=967
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生命周期内一直运行,但我还没用到项目中,不知道怎样才能保证只有一个任务运行,最近试一下能否同时进行多个任务。
参考
翻译版: Qt 线程基础(QThread、QtConcurrent等)