QThread 中的插槽同时运行

Slots inside QThread running concurrently

本文关键字:运行 插槽 QThread      更新时间:2023-10-16

我有两个类,一个处理程序和一个工作线程,通过信号和插槽连接。下面是一个简化版本(伪代码(:

处理程序构造函数:

Handler::Handler(QObject *parent) : QObject(parent)
{
    m_workerThread = new QThread;
    m_worker = new Worker;
    m_worker->moveToThread(m_workerThread);
    m_workerThread->start();
    connect(this, &Handler::doWork, m_worker, &Worker::doWork);
    connect(this, &Handler::stopWork, m_worker, &Worker::stopWork);
    emit doWork();
}

工人

void Worker::doWork()
{
    qDebug()<<"------"
    qDebug()<<"Start do work executing in: "<<QThread::currentThreadId();
    //doing some work
    m_timer->start();//a singleshot timer that calls doWork() on timeout
    qDebug()<<"work done";
    qDebug()<<"------" 
}
void Worker::stopWork()
{
    qDebug()<<"Stop timer executing in: "<<QThread::currentThreadId();
    m_timer->stop(); 
}

所以基本上工作是在从处理程序发出"doWork"之后开始的。"doWork"插槽有一个单次计时器,可在一定时间后再次调用相同的函数。一段时间后,处理程序发出"停止工作"信号。这是我的调试输出:

------
Start do work executing in: 0x65602450
work done
------
------
Start do work executing in: 0x65602450
work done
------
------
Start do work executing in: 0x65602450
work done
------
------
Start do work executing in: 0x65602450
stop work emitted from handler in: 0x750a7000
Stop timer executing in: 0x65602450
work done
------
------
Start do work executing in: 0x65602450
work done
------
etc...

所以我不明白的是,我的工作线程怎么可能同时执行两个插槽(doWork 和 stopWork(?不应该发布停止工作信号并等待线程在执行"停止工作"插槽之前变为空闲吗?

不幸的是,我

无法使用最小的工作示例重现这一点,但我希望从我发布的代码中可以清楚地看到我缺少什么。

同样从我的测试中,我认为这种情况在 30-40% 的情况下都会发生。

使用 QMutex 和/或 QSemaphore 等线程同步类可能会解决您的问题。

例如,如果您在处理程序类中创建了一个 QMutex 并将其传递给工作线程:

Handler::Handler(QObject *parent) : QObject(parent)
{
    //Class variable : QMutex* mutex
    mutex = new QMutex();
    m_workerThread = new QThread;
    m_worker = new Worker;
    m_worker->moveToThread(m_workerThread);
    m_workerThread->start();
    connect(this, &Handler::doWork, m_worker, &Worker::doWork);
    connect(this, &Handler::stopWork, m_worker, &Worker::stopWork,Qt::BlockingQueuedConnection);
    emit doWork(mutex);
}

然后从另一个线程内部调用这个常见的 QMutex 锁定机制:

void Worker::doWork(QMutex* mutex)
{
    mutex->lock();
    qDebug()<<"------"
    qDebug()<<"Start do work executing in: "<<QThread::currentThreadId();
    //doing some work
    m_timer->start();//a singleshot timer that calls doWork() on timeout
    qDebug()<<"work done";
    qDebug()<<"------" 
    mutex->unlock();
}

并在 Handler 类中使用相同的逻辑,就在发出"停止"信号之前:

...
mutex->lock();
emit stopWork();
mutex->unlock();
...

这样,您可以避免不必要的并发。

这里的 BlockingQueuedConnection 标志可确保在停止作业完成之前主线程不会前进,因此在作业完成之前不会解锁互斥锁。

这是我在处理并发情况和数据竞赛时尝试使用的逻辑,我希望它也能对您有所帮助。