最近我研究QThread源代码,在qthreadpool.cpp文件中,我们可以找到以下代码:
void QThreadPoolThread::run()
{
QMutexLocker locker(&manager->mutex);
for(;;) {
QRunnable *r = runnable;
runnable = 0;
do {
if (r) {
const bool autoDelete = r->autoDelete();
// run the task
locker.unlock();
#ifndef QT_NO_EXCEPTIONS
try {
#endif
r->run();
#ifndef QT_NO_EXCEPTIONS
} catch (...) {
qWarning("Qt Concurrent has caught an exception thrown from a worker thread.\n"
"This is not supported, exceptions thrown in worker threads must be\n"
"caught before control returns to Qt Concurrent.");
registerTheadInactive();
throw;
}
#endif
locker.relock();
if (autoDelete && !--r->ref)
delete r;
}
// if too many threads are active, expire this thread
if (manager->tooManyThreadsActive())
break;
r = !manager->queue.isEmpty() ? manager->queue.takeFirst().first : 0;
} while (r != 0);
if (manager->isExiting) {
registerTheadInactive();
break;
}
// if too many threads are active, expire this thread
bool expired = manager->tooManyThreadsActive();
if (!expired) {
++manager->waitingThreads;
registerTheadInactive();
// wait for work, exiting after the expiry timeout is reached
expired = !manager->runnableReady.wait(locker.mutex(), manager->expiryTimeout);
++manager->activeThreads;
if (expired)
--manager->waitingThreads;
}
if (expired) {
manager->expiredThreads.enqueue(this);
registerTheadInactive();
break;
}
}
}
此代码段表示如果当前任务完成,runnableReady将等待一段时间(默认为30秒),如下所示:
manager->runnableReady.wait(locker.mutex(), manager->expiryTimeout);
在此期间,如果调用了QThreadPoolPrivate :: enqueueTask,则会将新的可运行任务添加到队列中,如下所示:
void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
{
if (runnable->autoDelete())
++runnable->ref;
// put it on the queue
QList<QPair<QRunnable *, int> >::iterator at =
qUpperBound(queue.begin(), queue.end(), priority);
queue.insert(at, qMakePair(runnable, priority));
runnableReady.wakeOne();
}
然后,QThreadPoolThread :: run中的runnableReady将被唤醒。但是,正如我们所看到的,QThreadPoolThread类中的成员变量“runnable”为NULL,因此QThreadPoolThread :: run将返回,这不是我们想要的!
在我看来,由于“runnableReady”被唤醒,线程应该在队列中获取新的runnable任务并执行它。通过这种方式,QThreadPool可以重用现有的线程。我错了吗?请帮忙。