QThread内部的插槽同时运行

时间:2017-02-09 08:46:51

标签: c++ qt qthread qtimer worker-thread

我有两个类,一个Handler和一个与信号和插槽连接的Worker。这是一个简化版本(伪代码):

Handler构造函数:

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(); 
}

所以基本上工作是在发出&#34; doWork&#34;之后开始的。来自Handler。 &#34; doWork&#34; slot有一个单声道定时器,在一段时间后再次调用相同的功能。 一段时间后,Handler发出了#stop; stopWork&#34;信号。 这是我的调试输出:

------
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)的?在执行&#34; stopWork&#34;之前,不应该发布stopWork信号并等待线程变为空闲状态。槽?

不幸的是,我无法使用最小的工作示例来重现这一点,但我希望从我发布的代码中明白我错过了什么。

同样从我的测试中我发现这种情况发生在30-40%的时间。

1 个答案:

答案 0 :(得分:0)

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

例如,如果您在Handler类中创建了一个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标志确保在没有停止作业完成的情况下主线程不会前进,因此在作业完成之前不会解锁互斥锁。

这是我在处理并发情况和数据竞争时尝试使用的逻辑,我希望它也能帮到你。