1. 程式人生 > 其它 >Qt 多執行緒

Qt 多執行緒

QThread 是Qt中一個對執行緒支援的核心的底層類。每個執行緒物件代表了一個執行的執行緒。

POINT 1:QThread的例項與普通的例項沒什麼不同,只是執行著的run()函式會不同。

例1:

class MThread :public QThread  
{   
public:   
    MThread();  
    ~MThread();  
    void run();  
    void foo();  
    ...  
      
};  
class MDialog :public QDialog  
{   
    ...  
    MThread 
*mythread; }; MDialog::MDialog() { mythread = new MThread; ... }

需要注意的是,在QT中,QThread物件的例項mythread是屬於建立它的執行緒(執行緒A,即MDialog所在的執行緒) 的,mythread的所有程式程式碼與資料都放在與MDialog相同的空間中.這時的mythread,就像任何普通的自己定義的類的例項一樣.但是在 呼叫mythread->start()之後,mythread的run()函式中的程式碼會在新的執行緒(執行緒B)中執行.在run()函式中宣告的 變數\例項化的物件,都屬於執行緒B.

當emit sigDialogSignal()時,是會在MDialog所在的執行緒A中執行的.因為mythread與MDialog同屬於一個執行緒, 這時thread可以看做一個普通類的例項.另外,因為connect函式的連線方式預設是自動連線,而對同屬於一個純種的兩個物件,自動連線會使用直接連線,即slot在發出signal的執行緒中立即執行.

例2:

#include "mthread.h"  
#include <QDebug>  
MThread::MThread(QObject *parent)  
    : QThread(parent)  
{   
    myTimer.start(
1); connect(&myTimer, SIGNAL(timeout()), this, SLOT(slotPrint())); }
MThread::
~MThread() { }
void MThread::run() { for (int i = 0; i < 100; ++i) { for (int j = 0 ; j < 10000; ++j) { qDebug()<<"---------"<<i; } } exec(); }
void MThread::slotPrint() { qDebug()<<"=============================="; }

執行後出現:

---------9

==============================================================

---------9

不能誤以為:在一個QThread類的派生類中,run()函式中的語句在執行時,可能被本執行緒定時器超時slot中斷. (錯誤)

事實上,slotPrint()在建立MThread的例項的執行緒中執行.

POINT 2:執行緒B中的物件要想接收執行緒A中的物件發來的signal, 必須進入exec(), 如在exec()前有死迴圈, 沒有進入exec(), 則執行緒B中的物件不會收到signal.

void MThread::run()  
{   
    while(1) {  
        dosomething();  //此迴圈永不退出  
    }  
    exec();             //如果此事件迴圈不能進入,剛此執行緒不會收到任何signal  
} 

POINT 3:執行緒A中的指標可指向執行緒B中建立的物件例項,這個例項屬於執行緒B. 指標僅僅是一個地址, 而物件例項的變數/程式碼等都屬於執行緒B.

例1:

class MThread : public QThread  
{   
    Q_OBJECT  
public:   
    MThread(QObject *parent = 0);  
    ~MThread();  
    void run();  
    MPrint *mprint;  
};   
void MThread::run()  
{   
    mprint = new MPrint;  
    exec();  
}   
//如此宣告,mprint所指向的物件屬於另一個執行緒. 

例2:

class MThread : public QThread  
{   
    Q_OBJECT  
 
public:   
    MThread(QObject *parent = 0);  
    ~MThread();  
    void run();  
    MPrint *mprint;  
private:   
    QTimer *myTimer;  
private slots:  
    void slotPrint();     
    void testFoo();  
};   
void MThread::run()  
{   
    myTimer = new QTimer;  
    mprint = new MPrint;  
    myTimer->setInterval(100);  
    connect(myTimer, SIGNAL(timeout()), this, SLOT(testFoo()), Qt::DirectConnection);  
    QTimer::singleShot(0, myTimer,SLOT(start()));  
    exec(); 
}

上這樣寫run(),myTimer在run()中new,即myTimer這個指標屬於舊執行緒,但myTimer所指向的QTimer例項的實體在新的執行緒中,testFoo()會在新執行緒中執行。

例3:

void MThread::run()  
{   
    QTimer myTimer;  
    mprint = new MPrint;  
    myTimer.setInterval(100);  
    connect(&myTimer, SIGNAL(timeout()), this, SLOT(testFoo()), Qt::DirectConnection);  
    QTimer::singleShot(0, &myTimer,SLOT(start()));  
    //testFoo();  
    exec();  
} 

以上這樣寫run(),myTimer在run()中宣告,即myTimer屬於新的執行緒,testFoo()也會在新執行緒中執行。

例4:

class MThread : public QThread  
{   
    Q_OBJECT  
public:   
    MThread(QObject *parent = 0);  
    ~MThread();  
    void run();  
    MPrint *mprint;  
private:   
    QTimer myTimer;  
private slots:  
    void slotPrint();     
    void testFoo();  
};   
void MThread::run()  
{   
    mprint = new MPrint;  
    myTimer.setInterval(100);  
    connect(&myTimer, SIGNAL(timeout()), this, SLOT(testFoo()));  
    QTimer::singleShot(0, &myTimer,SLOT(start()));  
    //testFoo();  
    exec();  
} 

以上這樣寫run(),testFoo()會在建立myTimer的老執行緒中執行.因為可以看到,mytimer和this(即mythread),都是在同一個執行緒中,只是在另一個執行緒中(run()),做了connect操作.

要注意的是,執行緒B中啟動執行緒A中的一個定時器,不能使用myTimer.start(),這樣啟動不了定時器.而應使用signal來觸發start()這個slot. QTimer::singleShot(0, &myTimer,SLOT(start()));

POINT 5:slot不會中斷同執行緒中的slot。

例1:

#include "mthread.h"  
#include <QDebug>  
MThread::MThread(QObject *parent)  
    : QThread(parent)  
{   
    myTimer.start(1);  
    connect(&myTimer, SIGNAL(timeout()), this, SLOT(slotPrint()));  
}   
MThread::~MThread()  
{
}   
void MThread::run()  
{   
    exec();  
}   
void MThread::slotPrint()  
{   
    qDebug()<<"===========================";  
    for (int i = 0; i < 100; ++i) {  
        for (int j = 0 ; j < 10000; ++j) {  
            qDebug()<<"---------"<<i;  
        }  
    }  
}  

slotPrint()函式執行完之後才會退出,說明slot不會中斷slot,一個slot在執行完之後才會執行下一個slot.

注意:slotPrint()在建立MThread例項的執行緒中執行(主執行緒).而不是使用thread->start()創建出的那個執行緒(子執行緒)

例2:

#include "mthread.h"  
#include <QDebug>  
MThread::MThread(QObject *parent)  
    : QThread(parent)  
{   
    myTimer.start(1);  
    connect(&myTimer, SIGNAL(timeout()), this, SLOT(slotPrint()));  
}   
MThread::~MThread()  
{
}   
void MThread::run()  
{   
    testFoo();  
    exec();  
}
void MThread::slotPrint()  
{   
    qDebug()<<"=======================";
}   
void MThread::testFoo()  
{   
    for (int i = 0; i < 100; ++i) {  
        for (int j = 0 ; j < 10000; ++j) {  
            qDebug()<<"---------"<<i;  
        }  
    }  
}  

以上程式碼中,slotPrint()與testFoo()會在兩個不同的執行緒中執行.

注意:只有gui執行緒即主執行緒可以呼叫gui相關的函式,其他執行緒不可以,例如非gui執行緒顯示提示對話方塊是不允許的。