0

我已经对一些几何图形进行了迭代生成。我使用VTK进行渲染。每次迭代后,我想显示(渲染)当前进度。我的方法按预期工作,直到最后 2 个线程挂起等待 QWaitCondition。它们被阻止,即使它们在 QWaitCondition 的队列中的状态被唤醒(通过调试器检查)。我怀疑 2 个线程的数量与我的处理器的 4 个内核有某种联系。

简化代码如下。我做错了什么以及如何解决?

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
}
4

2 回答 2

2

您的代码中至少存在一个缺陷。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。所以 Kamil Klimek是对的:你在没有持有的 mutex 上调用 wait

您应该删除构造函数中的锁定,并在调用条件之前锁定。无论你在哪里锁定renderlock,线程都不应该持有threadlock

于 2013-03-19T15:02:21.490 回答
0

问题是我每个线程需要一个 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
}
于 2013-03-20T16:12:28.687 回答