1. 程式人生 > >Qt在多線程中使用信號槽的示例

Qt在多線程中使用信號槽的示例

mov 函數 cout this [] rdb 默認 emit etl

  之前對線程理解得不深入,所以對Qt的線程機制沒有搞清楚,今天寫一篇文章總結一下,如有錯誤,歡迎指出。

首先需要理解線程是什麽,線程在代碼中的表現其實就是一個函數,只不過這個函數和主線程的函數同時運行,寫C語言的都知道,一般代碼是從main()函數開始運行的,每個線程都有一個入口函數,main()函數可以看做是主線程的入口函數,從main函數開始執行,主線程就開始了,寫過一點代碼的都知道,程序是從main()函數開始一條一條地往下執行的,但是有的時候我們需要同時執行A,B兩個函數。初學代碼時,都是先調用A函數,再調用B函數,這時是A函數執行完畢後再執行B函數,沒有達到我們想要同時執行的目的。這時如果把A函數放入另一個線程中執行,那麽不需要等到A函數執行完畢,B函數就可以開始執行。C++11中已經有了多線程庫,簡單示例如下

std::thread t(A);
B();

你可以隨便寫兩個函數,函數名為A,B。函數內容可以寫成輸出10000次A(B函數可以輸出10000次B,輸出次數少了可能會觀察不到),你可能會發現A和B會交替出現,這就是A和B在同時執行的證明。多線程在圖形界面程序中幾乎是必須的,圖形界面程序的主線程一般是界面線程,用於響應用戶的操作,後臺線程用於執行計算,通信等操作,如果不使用多線程,圖形界面會因為等待計算數據(當然我說的是大量數據,少量數據你可能會感受不到)而卡住不響應用戶的操作。講到這裏你也許對線程有了一個基本的印象。

Qt使用QThread類有兩種方式,這個網上可以找到很多資料。

第一種:繼承QThread類,自己寫一個類(假設為MyThread),重寫QThread的run()函數,新線程就會運行run()裏面的代碼,但是要註意的是只有run()函數裏面的代碼在新線程裏運行,所以你自己的MyThread類裏面的槽函數雖然和主線程的信號綁定了,但是只要沒有放在run()裏面運行,還是運行在主線程中的。

第二種:使用moveToThread(),我下面的示例代碼就是使用的moveToThread()方法。QThrad中默認的run()函數啟用了事件循環(exec()),所以你移動到線程中的那個對象的所有槽函數均在新線程中執行,不會阻塞主線程。看完示例就明白了。新建一個QApplication工程,把mainwindow.h改成下面的代碼

// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H #include <QApplication> #include <QObject> #include <QEvent> #include
<thread> #include <iostream> #include <QThread> #include <QPushButton> #include <QVBoxLayout> #include <QMainWindow> // network thread class NetworkThread:public QObject { Q_OBJECT public: NetworkThread() { // do some initial works; } signals: void datacoming(int a); private: void memberFun(); public slots: void testSlot(); void sleepSlot(); }; // network thread class // GUI class,run in main thread class MyWidget:public QMainWindow { Q_OBJECT public: QPushButton *firstButton,*secondButton,*thirdButton; QVBoxLayout *layout; QWidget *p; public: explicit MyWidget(QWidget *parent); ~MyWidget(); signals: void signalTestStart(int a=0); void startNetworkSleep(); public slots: void secondButtonClicked(); }; // GUI class // csApplication class class csApplication:public QApplication { Q_OBJECT public: csApplication(int argc,char *argv[]); MyWidget *mywindow; NetworkThread *netthd; QThread *t; ~csApplication(); }; // csApplication class #endif // MAINWINDOW_H

把main.cpp改成下面的代碼,並刪去mainwindow.cpp

// main.cpp

#include "mainwindow.h"



// network thread

void NetworkThread::testSlot()
{
    std::cout<<"\nin testSlot()\n thread id:"<<std::this_thread::get_id()<<std::endl;
    memberFun();
}

void NetworkThread::memberFun()
{
    std::cout<<"\nin NetworkThread::memberFun()\nthread id:"<<std::this_thread::get_id()<<"\n"<<std::endl;
}

void NetworkThread::sleepSlot()
{
    std::cout<<"in NetworkThread::sleepSlot()\n thread id:"<<std::this_thread::get_id()<<"\nthen sleep 5 seconds\n";
    QThread::sleep(5);
    std::cout<<"sleepSlot() weak up\n"<<std::endl;
}

// network thread class

// GUI class,run in main thread

MyWidget::MyWidget(QWidget *parent=0)
{
    firstButton = new QPushButton(tr("first"));
    secondButton = new QPushButton(tr("second"));
    thirdButton = new QPushButton(tr("third"));

    layout = new QVBoxLayout;
    layout->addWidget(firstButton);
    layout->addWidget(secondButton);
    layout->addWidget(thirdButton);

    p=new QWidget;

    p->setLayout(layout);

    setCentralWidget(p);

}
MyWidget::~MyWidget()
{
    delete firstButton;
    delete secondButton;
    delete thirdButton;
    delete p;
    delete layout;
}

void MyWidget::secondButtonClicked()
{
    emit startNetworkSleep();
    std::cout<<"in MyWidget::secondButtonClicked()\n thread id: "<<std::this_thread::get_id()<<"\n"<<std::endl;
}

// GUI class

// csApplication class

csApplication::csApplication(int argc, char *argv[]):QApplication(argc,argv)
{

    std::cout<<"\nin csApplication()\nthread id:"<<std::this_thread::get_id()<<"\n"<<std::endl;
    mywindow = new MyWidget();
    netthd = new NetworkThread();
    t = new QThread();

    connect(mywindow->firstButton,SIGNAL(clicked()),netthd,SLOT(testSlot()),Qt::QueuedConnection);
    connect(mywindow->secondButton,SIGNAL(clicked()),mywindow,SLOT(secondButtonClicked()));
    connect(mywindow,SIGNAL(startNetworkSleep()),netthd,SLOT(sleepSlot()),Qt::QueuedConnection);

    netthd->moveToThread(t);
    t->start();
    mywindow->show();

}
csApplication::~csApplication()
{
    delete mywindow;
    delete netthd;
    delete t;
}

// csApplication class


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

    return a.exec();
}

點擊運行就可以了。

Qt在多線程中使用信號槽的示例