2 потока остались в ожидании на QWaitCondition, несмотря на вызовы wakeAll

У меня была многопоточная итеративная генерация некоторых геометрий. Я использую VTK для рендеринга. После каждой итерации я хотел бы отображать (отображать) текущий прогресс. Мой подход работает, как и ожидалось, пока последние 2 потока не будут зависать в ожидании QWaitCondition. Они заблокированы, даже если их состояние в очереди QWaitCondition — wokenUp (проверяется с помощью отладчика). Я подозреваю, что число двух потоков так или иначе связано с четырьмя ядрами моего процессора.

Упрощенный код ниже. Что я делаю не так и как это исправить?

class Logic
{
QMutex threadLock, renderLock;
//SOLUTION: renderLock should be per thread, not global like this!
QWaitCondition wc;
bool render;
...
}

Logic::Logic()
{
...
renderLock.lock(); //needs to be locked for QWaitCondition
}

void Logic::timerProc()
{
static int count=0;
if (render||count>10) //render wanted or not rendered in a while
{
threadLock.lock();
vtkRenderWindow->Render();
render=false;
count=0;
wc.wakeAll();
threadLock.unlock();
}
else
count++;
}

double Logic::makeMesh(int meshIndex)
{
while (notFinished)
{
...(calculate g)
threadLock.lock(); //lock scene
mesh[meshIndex]->setGeometry(g);
render=true;
threadLock.unlock();
wc.wait(&renderLock); //wait until rendered
}
return g.size;
}

void Logic::makeAllMeshes()
{
vector<QFuture<double>> r;
for (int i=0; i<meshes.size(); i++)
{
QFuture<double> future = QtConcurrent::run<double>(this, &Logic::makeMesh, i);
r.push_back(future);
}

while(any r is not finished)
QApplication::processEvents(); //give timer a chance
}

0

Решение

В вашем коде есть как минимум один дефект. count а также render принадлежат к критическому разделу, что означает, что они должны быть защищены от одновременного доступа.

Предположим, есть еще темы, ожидающие wc.wait(&renderLock);, Кто-то где-то казнит wc.wakeAll();, ВСЕ темы проснулись. Предположим, что хотя бы один поток видит notFinished как истина (если любой ваш код имеет смысл, это должен возможно) и вернитесь к выполнению:

    threadLock.lock(); //lock scene
mesh[meshIndex]->setGeometry(g);
render=true;
threadLock.unlock();
wc.wait(&renderLock) <----OOPS...

Во второй раз возвращается нить, у него нет блокировки renderLock, Так Камил Климек правильно: Вы звоните, подождите, на мьютекс, который вы не держите.

Вы должны снять блокировку в конструкторе и заблокировать перед вызовом условия. Где бы вы ни заблокировали renderlockнить не должна держать threadlock,

2

Другие решения

Подвох в том, что мне нужен был один QMutex на поток, а не один глобальный QMutex. Исправленный код ниже. Спасибо за помощь UmNyobe!

class Logic
{
QMutex threadLock;
QWaitCondition wc;
bool render;
...
}

//nothing in constructor related to threading

void Logic::timerProc()
{
//count was a debugging workaround and is not needed
if (render)
{
threadLock.lock();
vtkRenderWindow->Render();
render=false;
wc.wakeAll();
threadLock.unlock();
}
}

double Logic::makeMesh(int meshIndex)
{
QMutex renderLock; //fix
renderLock.lock(); //fix
while (notFinished)
{
...(calculate g)
threadLock.lock(); //lock scene
mesh[meshIndex]->setGeometry(g);
render=true;
threadLock.unlock();
wc.wait(&renderLock); //wait until rendered
}
return g.size;
}

void Logic::makeAllMeshes()
{
vector<QFuture<double>> r;
for (int i=0; i<meshes.size(); i++)
{
QFuture<double> future = QtConcurrent::run<double>(this, &Logic::makeMesh, i);
r.push_back(future);
}

while(any r is not finished)
QApplication::processEvents(); //give timer a chance
}
0

По вопросам рекламы [email protected]