1

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

处理程序构造函数:

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

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

------
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)?不应该在执行“stopWork”插槽之前发布 stopWork 信号并等待线程变为空闲吗?

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

同样根据我的测试,我认为这发生在 30-40% 的时间里。

4

1 回答 1

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 标志可确保您的主线程在停止作业未完成的情况下不会前进,因此在作业完成之前不会解锁互斥锁。

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

于 2017-02-09T12:36:41.337 回答