2017-02-09 91 views
1

我有两个类Handler和一个Worker与信号和插槽连接。下面是一个简化版本(伪代码):同时运行的QThread中的插槽

处理程序构造:

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

不幸的是,我不能用最小的工作示例重现这一点,但我希望从我发布的代码中清楚明白我失踪的内容。

同样从我的测试中我发现这是30-40%的时间。

+1

在“Worker :: doWork”的“//做一些工作”部分会发生什么?如果在该代码中的任何时候,控制权交还(不过简单地)回到事件循环,那么我认为对于另一个排队信号开始处理是完全可能的。因此,您的插槽不是并行执行,而是实际嵌套。 –

+0

我正在调用其他类实现的一些函数。您能否给我举一些关于如何将控制交还给事件循环的例子?我可以比看看在我打电话的功能中是否是这种情况! – luffy

+0

非常感谢您的提示。你是对的。我发现我正在调用的代码是使用“QCoreApplication :: processEvents()” – luffy

回答

0

使用像QMutex和/或QSemaphore这样的线程同步类可以解决您的问题。

例如,如果你已经在你的处理程序类创建的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::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的锁机制

而且使用相同的逻辑在你的Handler类,你发出“停止”信号之前:

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

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

BlockingQueuedConnection标志在这里确保您的主线程不会在没有停止作业的情况下前进完成,因此在作业完成之前不会解锁互斥量。

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

+0

问题是这可能会产生死锁,因为我将尝试从相同的线程两次锁定互斥锁 – luffy

+0

对不起,我的坏。我没有意识到你通过单点定时器激活相同的插槽。 但是这并没有改变主要事实,你明显需要同步这些线程。 在这种情况下,您可以再次调用实际的'Worker :: doWork'插槽之前将'mutex-> unlock()'行添加到超时触发槽中。这样,你的工作线程就可以避免死锁。 –