简体   繁体   English

尽管有wakeAll调用,仍有2个线程在QWaitCondition上等待

[英]2 threads left hanging waiting on QWaitCondition in spite of wakeAll calls

I have threaded iterative generation of some geometries. 我有线程迭代生成一些几何。 I use VTK for rendering. 我使用VTK进行渲染。 After each iteration I would like to display (render) the current progress. 在每次迭代后,我想显示(渲染)当前进度。 My approach works as expected until the last 2 threads are left hanging waiting for QWaitCondition. 我的方法按预期工作,直到最后2个线程挂起等待QWaitCondition。 They are blocked, even though their status in QWaitCondition's queue is wokenUp (inspected through debugger). 它们被阻止,即使它们在QWaitCondition的队列中的状态是wokenUp(通过调试器检查)。 I suspect that number of 2 threads is somehow connected with my processor's 4 cores. 我怀疑2个线程的数量与我的处理器的4个内核有某种联系。

Simplified code is below. 简化代码如下。 What am I doing wrong and how to fix it? 我做错了什么以及如何解决?

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
}

There is at least one defect in your code. 您的代码中至少存在一个缺陷。 count and render belong to the critical section, which means they need to be protected from concurrent access. countrender属于临界区,这意味着需要保护它们不受并发访问的影响。

Assume there are more threads waiting on wc.wait(&renderLock); 假设有更多线程在wc.wait(&renderLock);上等待wc.wait(&renderLock); . Someone somewhere execute wc.wakeAll(); 有人在某处执行wc.wakeAll(); . ALL the threads are woken up. 所有线程都被唤醒了。 Assume at least one thread sees notFinished as true (if any of your code make sense, this must be possible) and go back to execute : 假设至少有一个线程看到notFinished为true(如果你的任何代码有意义,那一定是可能的)并返回执行:

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

The second time the thread comes back, he doesn't have the lock renderLock . 线程第二次回来时,他没有锁定renderLock So Kamil Klimek is right: you call wait on a mutex you don't hold . 所以卡米尔克利梅克是对的: 你打电话等待一个你没有持有的互斥锁

You should remove the lock in constructor, and lock before the calling the condition. 您应该在构造函数中删除锁,并在调用条件之前锁定。 Wherever you lock renderlock , the thread should not hold threadlock . 无论你在哪里锁定renderlock ,线程都不应该持有threadlock

The catch was that I needed one QMutex per thread, and not just one global QMutex. 问题在于我每个线程需要一个QMutex,而不仅仅是一个全局QMutex。 The corrected code is below. 更正后的代码如下。 Thanks for help UmNyobe! 感谢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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM