#include #include int fooCallCounter = 0; void foo() { fooCallCounter++; } int main(int argc, char *argv[]) { QThreadPool::globalInstance()->setMaxThreadCount(1); QThreadPool::globalInstance()->setExpiryTimeout(1000); qDebug() << "Beginning: ThreadPool.activeThreadCount():" << QThreadPool:: globalInstance()->activeThreadCount(); // activeThreadCount == 0 == allThreads(0) - expiredThreads(0) - waitingThreads(0) + reservedThreads(0) QThreadPool::globalInstance()->reserveThread(); // reservedThreads++ qDebug() << "After Step 1: ThreadPool.activeThreadCount():" << QThreadPool:: globalInstance()->activeThreadCount(); // activeThreadCount == 1 == allThreads(0) - expiredThreads(0) - waitingThreads(0) + reservedThreads(1) QtConcurrent::run(&foo); // allThreads gains a thread qDebug() << "After Step 2: ThreadPool.activeThreadCount():" << QThreadPool:: globalInstance()->activeThreadCount(); // activeThreadCount == 2 == allThreads(1) - expiredThreads(0) - waitingThreads(0) + reservedThreads(1) ::Sleep(100); // finish concurrent, waitingThreads++ qDebug() << "After Step 3: ThreadPool.activeThreadCount():" << QThreadPool:: globalInstance()->activeThreadCount(); // activeThreadCount == 1 == allThreads(1) - expiredThreads(0) - waitingThreads(1) + reservedThreads(1) // In the next QtConcurrent::run(...), QThreadPoolPrivate tryStart will not succeed b/c // activeThreadCount() >= maxThreadCount() ( // Because tryStart doesn't succeed, the waitingThread count will not be decreased. // B/c tryStart doesn't succeed, enqueueTask will be called. EnqueueTask will call wakeOne. wakeOne will wake // the one waiting thread. However, the waitingThread count will not be decreased. QtConcurrent::run(&foo); qDebug() << "After Step 4: ThreadPool.activeThreadCount():" << QThreadPool:: globalInstance()->activeThreadCount(); // activeThreadCount == 1 == allThreads(1) - expiredThreads(0) - waitingThreads(1) + reservedThreads(1) // Once the second QtConcurrent::run(...) completes, the thread will start waiting again, increasing the waitingThread count. ::Sleep(100); // finish concurrent, waitingThread++ qDebug() << "After Step 5: ThreadPool.activeThreadCount():" << QThreadPool:: globalInstance()->activeThreadCount(); // activeThreadCount == 0 == allThreads(1) - expiredThreads(0) - waitingThreads(2) + reservedThreads(1) QThreadPool::globalInstance()->releaseThread(); // reservedThreads-- qDebug() << "After Step 6/Problem state: ThreadPool.activeThreadCount():" << QThreadPool:: globalInstance()->activeThreadCount(); // activeThreadCount == -1 == allThreads(1) - expiredThreads(0) - waitingThreads(2) + reservedThreads(0) ::Sleep(1500); // finish concurrent, waitingThread++ qDebug() << "After expiry: ThreadPool.activeThreadCount():" << QThreadPool:: globalInstance()->activeThreadCount(); // activeThreadCount == -1 == allThreads(1) - expiredThreads(1) - waitingThreads(1) + reservedThreads(0) // In the next QtConcurrent::run(...), QThreadPool thinks there is a thread waiting to be used // and so will decrement the waitingThreads count, and then enqueue the task (thus calling wakeOne()). // However, there are no threads actually running/waiting, and so nothing will happen. QtConcurrent::run(&foo); ::Sleep(500); if (fooCallCounter != 3) { qDebug() << "Foo was not called the third time. fooCallCounter:" << fooCallCounter; } qDebug() << "Before forth call to run(): ThreadPool.activeThreadCount():" << QThreadPool:: globalInstance()->activeThreadCount(); // activeThreadCount == 0 == allThreads(1) - expiredThreads(1) - waitingThreads(0) + reservedThreads(0) // Now that waitingThreads is back to the correct value, we can start new tasks again. BOTH calls to foo should be run now. QtConcurrent::run(&foo); ::Sleep(500); if (fooCallCounter != 4) { qDebug() << "Unexpected value for fooCallCounter:" << fooCallCounter; } qDebug() << "After forth call to run(): ThreadPool.activeThreadCount():" << QThreadPool:: globalInstance()->activeThreadCount(); // activeThreadCount == 0 == allThreads(1) - expiredThreads(0) - waitingThreads(1) + reservedThreads(0) return 0; }