在QThreadPool
的Qt 4.7参考中,我们发现:
void QThreadPool::releaseThread()
释放先前通过调用
reserveThread()
保留的线程。注意:调用此函数而不先保留线程 暂时增加
maxThreadCount()
。 这在线程时很有用 入睡等待更多的工作,允许其他线程 继续。请务必在等待完成后致电reserveThread()
,以便 线程池可以正确维护activeThreadCount()
。另请参阅
reserveThread()
。
void QThreadPool::reserveThread()
保留一个线程,而忽略
activeThreadCount()
和maxThreadCount()
。使用完线程后,调用
releaseThread()
允许它 可以重复使用。注意:此函数将始终增加活动线程的数量。 这意味着通过使用此功能,可以
activeThreadCount()
返回大于maxThreadCount()
的值。另请参阅
releaseThread()
。
我想使用releaseThread()
使使用嵌套的并发映射成为可能,但是在以下代码中,它挂在waitForFinished()
中:
#include <QApplication>
#include <QMainWindow>
#include <QtConcurrentMap>
#include <QtConcurrentRun>
#include <QFuture>
#include <QThreadPool>
#include <QtTest/QTest>
#include <QFutureSynchronizer>
struct Task2 { // only calculation
typedef void result_type;
void operator()(int count) {
int k = 0;
for (int i = 0; i < count * 10; ++i) {
for (int j = 0; j < count * 10; ++j) {
k++;
}
}
assert(k >= 0);
}
};
struct Task1 { // will launch some other concurrent map
typedef void result_type;
void operator()(int count) {
QVector<int> vec;
for (int i = 0; i < 5; ++i) {
vec.push_back(i+count);
}
Task2 task;
QFuture<void> f = QtConcurrent::map(vec.begin(), vec.end(), task);
{
// with out releaseThread before wait, it will hang directly
QThreadPool::globalInstance()->releaseThread();
f.waitForFinished(); // BUG: may hang there
QThreadPool::globalInstance()->reserveThread();
}
}
};
int main() {
QThreadPool* gtpool = QThreadPool::globalInstance();
gtpool->setExpiryTimeout(50);
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;
}
此代码不会永远运行;它将在许多循环(1〜10000)后挂起,所有线程都在等待条件变量。
我的问题是:
dev env:
Linux版本2.6.32-696.18.7.el6.x86_64; Qt4.7.4; GCC 3.4.5
Windows 7; Qt4.7.4; mingw 4.4.0
答案 0 :(得分:1)
当您尝试处理expiryTimeout时,由于QThreadPool中的竞争条件,程序挂起。这是详细的分析:
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。 如果计划在 线程到期的时间恰好相同,那么线程将看到 假,将过期。但是主线程不会重新启动其他任何线程 线。这可能会使应用程序挂起,因为任务永远不会挂起 运行。
快速的解决方法是使用较长的expiryTime(默认为30000)并删除等待线程过期的while循环。
这里是修改后的主要功能,程序在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;
}
答案 1 :(得分:1)