多線程中的信號與槽(中)


令人不解的問題:

當槽函數是線程類的成員時,為什么依然不在本線程內被調用執行?

隱藏的問題:
對象依附於哪一個線程?
對象的依附性與槽函數執行的關系?
對象的依附性是否可以改變?

對象依附於哪個線程?
默認情況下,對象依附於自身被創建的線程
例如:對象在主線程(main()函數)中被創建,則依附於主線程

int main(int argc, char* argv[]) { //...
   TestThread t;     //依附於主線程
   MyObject m;       //依附於主線程
}

對象的依附性與槽函數執行的關系?
默認情況下,槽函數在其所依附的線程中被調用執行

int main(int argc, char* argv[]) { //...
   TestThread t;     //依附於主線程
   MyObject m;       //依附於主線程 //下面連接中的槽函數都在主線程中被調用執行
   QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted())); QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot())); }

對象的依附性是否可以改變?
QObject::moveToThread用於改變對象的線程的依附性,使得對象的槽函數在依附的線程中被調用執行

int main(int argc, char* argv[]) { //...
   TestThread t;     //依附於主線程
   MyObject m;       //依附於主線程 //改變對象m的線程的依附性,使其依附於線程t
   m.moveToThread(&t) }

對象的依附性

MyObject.h

#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>


class MyObject : public QObject
{
    Q_OBJECT
public:
    MyObject();
protected slots:
    void getStarted();
    void testSlot();
};

#endif // MYOBJECT_H
View Code

TestThread.h

#ifndef TESTTHREAD_H
#define TESTTHREAD_H

#include <QThread>

class TestThread : public QThread
{
    Q_OBJECT
protected:
    void run();
public:
    TestThread();

signals:
    void testSignal();
protected slots:
    void testSlot();
};

#endif // TESTTHREAD_H
View Code

MyObject.cpp

#include "MyObject.h"
#include <QObject>
#include <QThread>
#include <QDebug>

MyObject::MyObject()
{

}

void MyObject::getStarted()
{
    qDebug() <<"void MyObject::getStarted()tid = " << QThread::currentThreadId() ;
}

void MyObject::testSlot()
{
    qDebug() << "void MyObject::testSlot() " << QThread::currentThreadId() ;
}
View Code

TestThread.cpp

#include "TestThread.h"
#include <QDebug>

TestThread::TestThread()
{
    connect(this,SIGNAL(testSignal()),this,SLOT(testSlot()));
}

void TestThread::run()
{
   qDebug() << "void TestThread::run() begin...tid = " << currentThreadId();

   for(int i=0; i<10; i++)
   {
       qDebug() << "void TestThread::run() i = " << i;
       sleep(1);
   }

   emit testSignal(); //發射的信號誰來接收呢,可以在構造函數中將信號和槽函數進行關聯。

   qDebug() << "void TestThread::run() end...";
}

void TestThread::testSlot()
{
    qDebug() << "void TestThread::testSlot() tid = " << currentThreadId();
}
View Code

main.cpp

#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include "TestThread.h"
#include "MyObject.h"


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main tid() " << QThread::currentThreadId();

    TestThread t;
    MyObject m;

    QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted()));
    QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()));

    m.moveToThread(&t);

    t.start();
    return a.exec();
}
View Code

 

課程中使用的是Qt4,MyObject相關的槽函數沒有被調用,但是使用Qt5.4,MyObject相關的槽函數被調用了。

下面分析沒有被調用的情況:

問題:
實驗中對象m的槽函數為什么沒有全部被執行?

線程中的事件循環
信號與槽的機制需要事件循環的支持
QThread類中提供的exec()函數用於開啟線程的事件循環
只有開啟事件循環,槽函數才能在信號發送后被調用

信號的發送是隨時隨地都可以完成的,發送完成后,信號就到事件隊列中去了。信號進入事件隊列中去有什么用呢?
沒人理它,它什么用都沒有。如何處理事件隊列呢?此時事件循環就派上用場了。
但凡通過exec開啟了事件循環,就會不停的從事件隊列中取信號,取到信號后就會去判斷該信號有沒有關聯相關的槽函數,
如果有對應的槽函數,則調用相應的槽函數。

想要槽函數在指定的線程中被調用,需要在指定的線程中調用exec函數,開啟事件循環。
小結論:
前提條件
對象依附的線程開啟了事件循環
后置結果
對象中的槽函數在依附的線程中被調用執行

只需要在TestThread.cpp的run函數中加上exec函數即可

void TestThread::run()
{
   qDebug() << "void TestThread::run() begin...tid = " << currentThreadId();

   for(int i=0; i<10; i++)
   {
       qDebug() << "void TestThread::run() i = " << i;
       sleep(1);
   }

   emit testSignal(); //發射的信號誰來接收呢,可以在構造函數中將信號和槽函數進行關聯。

   exec();
   qDebug() << "void TestThread::run() end...";
}
View Code

 從打印結果看,與上面沒有使用exec的執行結果並無不同,很可能是因為版本不同造成的。

從打印結果看,MyObject的兩個槽函數都被調用了,且是在依附於t的那個線程。但是t的槽函數還是依附於主線程,我也想讓t的槽函數依附t,怎么操作?非常簡單,只需要在main.cpp中的main函數中,加入t.moveToThread(&t)即可,如下所示:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main tid() " << QThread::currentThreadId();

    TestThread t;
    MyObject m;

    QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted()));
    QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()));

    m.moveToThread(&t);
    t.moveToThread(&t);

    t.start();
    return a.exec();
}
View Code

研究槽函數的具體執行線程有什么意義?
當信號的發送與對應槽函數的執行在不同線程中,可能產生臨界資源的競爭問題

比如說,在run函數對某一個臨界資源進行修改,在槽函數中也對臨界資源進行修改,槽函數的調用是在另一個線程中完成的,此時調用槽函數的線程和本身的線程就可能產生競爭問題。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM