Программа зависает из-за состояния гонки в QThreadPool при попытке справиться с expiryTimeout.Вот подробный анализ:
Проблема в QThreadPool - source
При запуске задачи QThreadPool что-то делал построки:
QMutexLocker locker(&mutex);
taskQueue.append(task); // Place the task on the task queue
if (waitingThreads > 0) {
// there are already running idle thread. They are waiting on the 'runnableReady'
// QWaitCondition. Wake one up them up.
waitingThreads--;
runnableReady.wakeOne();
} else if (runningThreadCount < maxThreadCount) {
startNewThread(task);
}
И основной цикл потока выглядит так:
void QThreadPoolThread::run()
{
QMutexLocker locker(&manager->mutex);
while (true) {
/* ... */
if (manager->taskQueue.isEmpty()) {
// no pending task, wait for one.
bool expired = !manager->runnableReady.wait(locker.mutex(),
manager->expiryTimeout);
if (expired) {
manager->runningThreadCount--;
return;
} else {
continue;
}
}
QRunnable *r = manager->taskQueue.takeFirst();
// run the task
locker.unlock();
r->run();
locker.relock();
}
}
Идея состоит в том, что поток будетподождать заданное количество секунд для задачи, но если за заданный промежуток времени задача не была добавлена, поток истекает и завершается.Проблема в том, что мы полагаемся на возвращаемое значение runnableReady. Если есть задача, которая запланирована точно в то же время, когда истекает поток, то поток увидит false и истечет.Но основной поток не будет перезапускать любой другой поток.Это может привести к зависанию приложения, поскольку задача никогда не будет запущена.
Быстрый обходной путь - использовать длинный expiryTime (по умолчанию 30000) и удалить цикл while, который ожидает потоки.expired.
Вот модифицированная основная функция, программа гладко работает в Windows 7, по умолчанию используются 4 потока:
int main() {
QThreadPool* gtpool = QThreadPool::globalInstance();
//gtpool->setExpiryTimeout(50); <-- don't set the expiry Timeout, use the default one.
qDebug() << gtpool->maxThreadCount();
int count = 0;
for (;;) {
QVector<int> vec;
for (int i = 0; i < 40 ; i++) {
vec.push_back(i);
}
// launch a task with nested map
Task1 task; // Task1 will have nested concurrent map
QFuture<void> f = QtConcurrent::map(vec.begin(), vec.end(),task);
f.waitForFinished(); // BUG: may hang there
count++;
/*
// waiting most of thread in thread pool expire
while (QThreadPool::globalInstance()->activeThreadCount() > 0)
{
QTest::qSleep(50);
}
*/
// launch a task only calculation
Task2 task2;
QFuture<void> f2 = QtConcurrent::map(vec.begin(), vec.end(), task2);
f2.waitForFinished(); // BUG: may hang there
qDebug() << count ;
}
return 0;
}