2017-02-09 87 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()'行添加到超時觸發槽中。這樣,你的工作線程就可以避免死鎖。 –