唤醒线程使用QWaitCondition

4
我的Qt应用程序有一个工作线程,当有新数据可用时会处理它。到目前为止,工作线程使用QWaitCondition来在新数据可用时唤醒。
但是,只有在线程通过wait()主动等待时,QWaitCondition才会被触发。可能会出现这样的情况:当线程仍在工作或者即将运行到下一个wait()命令时,新数据已经可用了。
我正在寻找类似于MFC中的CEvent的东西,在任何时间都可以“记住”新数据通知,并且如果自上次wait()调用以来已经发出新数据信号,则不会等待。在Qt中实现这个功能的最佳方法是什么?
谢谢, Fabian

你可以使用 QSemaphore。 - Martin James
2个回答

2
如果其他线程可以在有新数据时触发信号,你可以实现一个简单的调度器类来在适当的时间唤醒你的线程(使用信号/槽,就像@ratchet freak建议的那样):
class Data;
class Scheduler : public QObject
{
Q_OBJECT
public:

Scheduler(QTread* workerIn) : worker(workerIn) {};

Q_SLOT void OnNewData(Data* data)
{
QMutexLocker lock(&mutex);
while(worker->isRunning())
   condition.wait(&mutex);

Q_EMIT startWorker(data);
}; 

Q_SLOT void OnThreadFinished() 
{
     condition.wakeOne();
}

Q_SIGNAL void startWorker(Data* data);    

private:
QThread* worker;
QWaitCondition condition;
QMutex mutex;
};

如果在worker运行时出现多个数据值,它们将被qt事件队列正确地排队。调度程序必须位于自己的线程中。启动worker信号必须连接到worker线程中的适当槽,worker线程的完成槽必须连接到OnThreadFinished()槽。

谢谢。不过,我认为我不需要一个专门的调度程序来完成这个任务... 在我的情况下听起来有点过度设计了。 - Fabian
@Fabian,这取决于你的想法,我非常喜欢“模块化越好”的概念——宁愿有10个小类/函数,也不要一个大的。因此,只要某些功能可以轻松封装,实际上封装它永远不会过度。专用调度程序不会占用任何资源,同时它允许将所有调度保持在一个地方,并且可以在需要时重复使用于任何工作器,还可以使工作器代码更简洁。 - Ilya Kobelevskiy
你的论点很有道理。我会将你的答案标记为解决方案。感谢所有的反馈。 - Fabian

1

QWaitCondition应与QMutex一起使用。

public void Worker::add(Data data)
{
    QMutexLocker lock(&mutex);
    //add data
    condition.wakeOne();
}


public void Worker::run()
{
    while(true)
    {
        Data data;
        {
            // no other thread will be able to trigger a wake while inside this block
            QMutexLocker lock(&mutex);
            if(shouldStop)return;
            while(!hasAvailable()) 
                condition.wait(&mutex);
            data = removeOne();
        }
        //do something with data;

    }
}

这样,如果有新的数据包到来,线程就不会等待,而在 while 循环检查和等待调用之间,wakeOne 不会被另一个线程触发。
更好的选择是使用信号和槽。

谢谢。如果“数据”在线程中本地存储,那么它可以跳过等待,如果数据仍然存在,则可以正常工作。在我的情况下,收集要处理的数据更加棘手,并且只有在唤醒线程后才会发生。在这种情况下,如果我理解正确的话,上述代码将“忘记”在“使用数据进行某些操作”部分内触发的唤醒调用。 - Fabian
更好的选择是使用信号和槽。这可能确实是最简单的解决方案。提供一个用于接收新数据通知的槽(bNewData = true),并在没有新数据可用时简单地在 while 循环中休眠。 - Fabian
实际上,如果存在事件循环,则不需要睡眠,因为当信号到达时,事件循环会自行唤醒。如果没有事件循环,则插槽不会被触发。 - ratchet freak
尽管排队的信号槽连接始终是首选,但这是唤醒通过QThreadPool+QRunnable模式实现的工作程序常见问题的合理解决方案。由于QRunnable并未子类化QObject,因此无法使用排队的信号槽连接来唤醒基于QRunnable的工作程序。在这种特定情况下,QMutex+QWaitCondition可能是最不疯狂的解决方案。 - Cecil Curry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接