Qt QTread 背景知识
1、moveToThread
QObject worker;
worker.moveToThread(_thread);
这个 worker 不能指定parent,否则 moveToThread 会失败。
同样,如果 worker moveToThread 后,不能设置非同一线程下的 parent。
即,对象树下的所有对象、都必须在同一线程里,否则无论是修改对象树、还是修改线程,都会失败。
2、QThread 对象释放掉,但线程未必停止了。正确的停止方式:
首先请求中断任务,QThread 在 run时,可能会存有多个待执行的任务,用 while 循环来一个个执行的
因此,QThread 对象释放时,先请求中断 requestInterruption,run 中的 while 则判断是否有中断
while(!isInterruptionRequested())
requestInterruption();
// 然后 发出 quit event
quit();
// 等待退出 QThread 的 event loop
wait();
}
3、实例分析
telegram 的 taskqueue, 对 task 的执行、包括结束,都是在同一个 worker 工作函数里执行的。
主线程生成 task,加入到 taskqueue,taskqueue 通过信号 taskadded,通知 worker 开始执行任务, worker 从 task 队列取出 task,调用 task->process(),完成后,发出 worker 的 taskFinished 信号,不在同一线程、所以 生成 event 插入到 主线程的 event loop 里,然后主动调用 QCoreApplication::processEvents(),这时候 worker 的 taskFinished 信号会被处理掉,即,一个任务 process 后,立刻 finished。
telegram worker的本意是,主线程添加 task 至 taskqueue ,将 task 加入到 task 列表、然后发出 taskadded 信号就结束了,work 的工作函数会被 taskadded 信号唤醒,工作函数 while 检查线程是否中断,否则从 task 列表里取出一个 task、然后 process、发出 finished 信号,再次检查线程是否中断。
这里的问题是,主线程可能在上个 task 还在执行的时候,如果再添加一个 task 进来、会塞 taskadded 事件到 event loop 里,那么上一个 work 工作函数在 task finish 之后、调用 QCoreApplication::processEvents() 的时候,会有两个 work 工作函数被执行,虽然 加了锁,不会循环执行相同的任务,但其实已经跑飞了。
一个 串行 的任务队列:
利用 QThread run 的 event loop,来实现任务调度。