Qt在多線程中使用信號槽的示例
之前對線程理解得不深入,所以對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在多線程中使用信號槽的示例