QT之深入理解QThread

QT之深入理解QThread

理解QThread之前需要了解下QThread類,QThread擁有的資源如下(摘錄于QT 5.1 幫助文檔):
在以上資源中,本文重點關注槽:start();信號:started()、finished();受保護的方法:run()、exec();
理解QThread
QThread與通常所熟知的線程(thread)有很大出入,在面向過程的語言中,我們建立一個線程的同時會傳入一個函數名,這個函數名代表該線程要執行的具體代碼(如圖 1 所示)。
技術分享
圖 1. 我們通常所理解的線程
但是QThread里并沒有線程的具體代碼,QThread只是一個接口而已,目的是為操作系統提供一個用于線程調度的“句柄”。這個“句柄”即是QThread的入口(如圖 2 所示)。
技術分享
圖 2. QThread是“面向對象的”
QThread的入口多種多樣,可以是槽函數,也可能是某個事件處理函數,但是由于是由系統調度的,因此這些函數的“準確”執行時刻是無法預知的。
QThread的出口是finished()信號。
作為線程,QThread會毫不猶豫的為自己創建一個運行空間,一個單獨的執行線索,一個新的線程,但是翻閱QThread所擁有的資源,我們找不到傳入函數名的地方,因此我們仿佛無法為這個新創建的線程提供具體的執行代碼。
很多人因此想到了run()方法,因而繼承QThread函數,并將自己的代碼寫在run()方法中,往往要求run()方法不可以立刻退出,因此加入循環體和wait()方法,有時候為了響應事件而調用exec()進行堵塞。但這種做法是不建議的,已有文章指出“QThread was designed and is intended to be used as an interface or a control point to an operating system thread, not as a place to put code that you want to run in a thread.?”具體參見:<http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/>。
? ??那么,QThread真的不能執行具體代碼么?如果不是,怎樣將要在新線程中執行的程序交付給QThread呢?答案是moveToThread()方法。任何基于QObject類的子類都具有該方法。某個對象被moveTo到新線程后,它所具有的槽函數和事件處理程序都會被移動到新線程所在的運行空間中,成為新線程與操作系統之間的接口,即成為了新線程的入口。當有與這個槽連接的信號或與之相配的事件發生時,槽函數和事件處理程序將會在新線程空間中執行。
如果只到此為止,那么很容易出現另一個問題,也就是上面連接中所舉的例子。我們在這里詳細說明。程序如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class?MyThread :?public?QThread
{
public:
????MyThread()
????{
????????moveToThread(this);
????}
????void?run();
signals:
????void?progress(int);
????void?dataReady(QByteArray);
public?slots:
????void?doWork();
????void?timeoutHandler();
};
上面這段程序的問題在哪兒呢?正如原文所說:“We’re telling the thread to run code “in itself”.We’re also doing this?before?the thread is running as well. Even though this seems to work, it’s confusing, and not how QThread was designed to be used (all of the functions in QThread were written and intended to be called from the creating thread, not the thread that QThread starts).”
總結起來,問題有兩點:1.在構造函數中moveToThread(),此時MyThread還沒有開始運行;2.將MyThread移動到它自己空間去運行后,我們失去了對MyThread的引用。以上兩點都容易導致非常致命的問題。可見,我們為了讓代碼在新線程中得以執行,我們實在有點兒太“不擇手段”了。
出現以上問題的根本原因在于,并沒有充分理解QThread只是一個接口的本質。那么應該如何正確的讓程序在新線程中得以執行呢?答案是將需要在新線程中運行的對象moveTo到QThread中,而非繼承QThread并把自身moveTo到新線程空間中。
由此我們提出應用QThread的以下幾個重要原則。
QThread應用原則:
1.QThread只是系統執行線程的接口而已,并不是用于編寫代碼的;
2.在當前線程(如:線程A)上下文中創建的對象屬于當前線程,其他線程(如:線程B、C、D...)不可以操作屬于當前線程(如:線程A)的對象;
3.當前線程(如:線程A)中基于OBject類的對象可以被移動到其他線程(如:線程B、C、D...);
4.當前線程(如:線程A)中基于OBject類的對象在移動到其他線程(如:線程B、C、D...)去執行的時候,要求目標線程(如:線程B、C、D...)已經開始運行;
由2可以推出,如果當前線程(如:線程A)中,基于OBject類的對象被移動到其他線程(如:線程B、C、D...)之后,該對象只能由目標線程(如:線程B、C、D...)負責釋放。
另外,在將信號與被moveTo到新線程中的對象所擁有的槽相連接時,需要注意連接的方式。
注意:
信號與槽的連接方式有:Qt::AutoConnection、Qt::DirectConnection、Qt::QueuedConnection和Qt::BlockingQueuedConnection。
Qt::AutoConnection:是根據對象所在線程不同而選擇Qt::DirectConnection或Qt::QueuedConnection;
Qt::DirectConnection:用于同一個線程當中,相當于直接函數調用,槽函數執行完后才返回;
Qt::QueuedConnection:用于不同的線程當中,會建立一個隊列,槽函數立即返回,而不用等待隊列中的信號執行完畢;
Qt::BlockingQueuedConnection:也是用于不同線程的,但是又相當于函數調用,因為要等到槽函數執行完畢才能夠返回。
示例:
在此,提供一個應用QThread的示例,該示例中打開一個串口用于接收數據,但為了同時兼顧UI對用戶的響應,需要為串口接收程序單獨建立一個線程。由于串口對象被moveTo到了新線程中,因此無法在UI線程中關閉串口,因此要用到QThread的finished()信號。
這只是一個示例,代碼的編寫更注重演示效果,而非其他。
該示例的工程組織如下:
技術分享
uiwindow.ui文件中窗體為初始化狀態。
Serial.pro 文件內容如下:
--------------------------------------------------------------------------
#-------------------------------------------------
#
# Project created by QtCreator 2014-07-18T15:41:22
#
#-------------------------------------------------
 
QT       += core gui
 
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
 
greaterThan(QT_MAJOR_VERSION, 4) {
    QT       += widgets serialport
} else {
    include($$QTSERIALPORT_PROJECT_ROOT/src/serialport/qt4support/serialport.prf)
}
 
TARGET = Serial
TEMPLATE = app
 
 
SOURCES += main.cpp\
        uiwindow.cpp \
    serial.cpp
 
HEADERS  += uiwindow.h \
    serial.h
 
FORMS    += uiwindow.ui
--------------------------------------------------------------------------
 
serial.h 文件內容如下:
--------------------------------------------------------------------------
#ifndef SERIAL_H
#define SERIAL_H
 
#include <QObject>
#include <QtSerialPort/QSerialPort>
 
class Serial : public QObject
{
    Q_OBJECT
public:
    explicit Serial(QObject *parent = 0);
    ~Serial(void);
    QSerialPort *port;
    
signals:
    
public slots:
    void readData(void);
    void threadStarted(void);
    void threadFinished(void);
    
};
 
#endif // SERIAL_H
--------------------------------------------------------------------------
 
serial.cpp 文件內容如下:
--------------------------------------------------------------------------
#include "serial.h"
#include <QMessageBox>
#include <QDebug>
#include <QThread>
 
Serial::Serial(QObject *parent) :
    QObject(parent)
{
    port = new QSerialPort();
    port->setPortName("COM1");
    if(!port->open(QSerialPort::ReadWrite))
    {
        QMessageBox WrrMsg;
        WrrMsg.setInformativeText("無法打開該串口");
        WrrMsg.show();
        WrrMsg.exec();
    }
    port->setBaudRate(QSerialPort::Baud19200,QSerialPort::AllDirections);   // 19200,N,8,1
    port->setDataBits(QSerialPort::Data8);
    port->setStopBits(QSerialPort::OneStop);
    port->setParity(QSerialPort::NoParity);
    port->setFlowControl(QSerialPort::NoFlowControl);
    connect(port, SIGNAL(readyRead()), this, SLOT(readData()), Qt::DirectConnection);   // 注意,真正執行時 port 與 Serial 在同一個線程中,因此使用 Qt::DirectConnection。
}
 
Serial::~Serial(void)
{
}
 
void Serial::readData(void)
{
    qDebug()<< "Reading Data...ID is:" << QThread::currentThreadId();
    port->clear(QSerialPort::AllDirections);
}
 
void Serial::threadStarted(void)
{
    qDebug()<< "Thread has started...ID is:" << QThread::currentThreadId();
}
 
void Serial::threadFinished(void)
{
    qDebug()<< "Closing COM port...ID is:" << QThread::currentThreadId();
    if(port->isOpen())
    {
        port->close();      // 關閉串口。
    }
}
--------------------------------------------------------------------------
 
uiwindow.h 文件內容如下:
--------------------------------------------------------------------------
#ifndef UIWINDOW_H
#define UIWINDOW_H
 
#include <QMainWindow>
#include <QThread>
#include "serial.h"
 
namespace Ui {
class UIWindow;
}
 
class UIWindow : public QMainWindow
{
    Q_OBJECT
    
public:
    explicit UIWindow(QWidget *parent = 0);
    ~UIWindow();
 
private:
    Ui::UIWindow *ui;
    QThread serialThread;
    Serial *serial;
};
 
#endif // UIWINDOW_H
--------------------------------------------------------------------------
 
uiwindow.cpp 文件內容如下:
--------------------------------------------------------------------------
#include "uiwindow.h"
#include "ui_uiwindow.h"
#include <QDebug>
 
UIWindow::UIWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::UIWindow)
{
    ui->setupUi(this);
 
    qDebug()<< "UI thread ID is:" << QThread::currentThreadId();
 
    serial = new Serial();
    connect(&serialThread, SIGNAL(started()), serial, SLOT(threadStarted()), Qt::QueuedConnection);     // 注意,serialThread 與 serial 并不在同一個線程中,因此使用 Qt::QueuedConnection。
    connect(&serialThread, SIGNAL(finished()), serial, SLOT(threadFinished()), Qt::DirectConnection);   // serialThread 的 finished() 信號是在新線程中執行的,因此此處要使用 Qt::DirectConnection。
 
    serialThread.start(QThread::HighestPriority);   // 開啟線程,串口接收線程的優先級較高。
    serial->moveToThread(&serialThread);            // 將串口接受對象移動到新線程中。
    serial->port->moveToThread(&serialThread);      // 用于接收的 port 一并移入新線程中。
}
 
UIWindow::~UIWindow()
{
    if(serialThread.isRunning())
    {
serialThread.exit();                // 結束該線程。
        serialThread.wait();
        /*while(!serialThread.isFinished())
        {
            ;
        }*/
    }
    delete ui;
}
--------------------------------------------------------------------------

http://blog.csdn.net/desert187/article/details/37932999

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/458615.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/458615.shtml
英文地址,請注明出處:http://en.pswp.cn/news/458615.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

常見人事問題

整體素養的常見問題 一、請你自我介紹一下你自己&#xff1f; &#xff08;面試官目的&#xff1a;深度了解求職者&#xff0c;看求職者基本的溝通和自我認知能力&#xff09; NO: 只說姓名、年齡、愛好等基本的信息后就沒了。只重復簡歷里的內容&#xff0c;如工作經驗就…

springMVC數據封裝成POJO

springMVC把前臺的數據封裝為POJO與struts2的封裝形式不同。struts2需要在控制器聲明需封裝的POJO&#xff0c;而springMVC不需要任何準備工作&#xff0c;只需在相應的方法的參數中加上需封裝的POJO&#xff0c;當用戶提交表單時&#xff0c;springMVC會根據表單中dom元素的na…

QT輸入輸出(四)之 QProcess

QProcess Qt提供了一個QProcess類用于啟動外部程序并與之通信.這個類是異步工作的&#xff0c;而且在后臺執行&#xff0c;這樣用戶界面就可以始終保持響應。 啟動一個新的進程的操作非常簡單,只需要將待啟動的程序名稱和啟動參數傳遞給start()函數即可. 例如&#xff1a; QObj…

iOS 推送 邏輯

推送 階段一&#xff1a;Provider[服務端]把要發送的消息&#xff0c;目的IOS設備標識打包&#xff0c;發送給APNS&#xff1b; 階段二&#xff1a;APNS在自身的已注冊Push服務的IOS設備列表中&#xff0c;查找有相應標識的IOS設備&#xff0c;并將消息發送到IOS設備&#xff1…

正則提取的url中的域名以及替換域名的方法 preg_match()和preg_replace()

<?php //網站的url$url http://www.baidu.com/index.php;//正則表達式$reg /(http):\/\/([^\/])/i;preg_match($reg, $url,$res);/** $res的結果array (size3)0 > string http://www.baidu.com (length20)1 > string http (length4)2 > string www.baidu.com (…

音視頻開發

command s 截模擬器的屏幕 MP.4只是一個容器 H.264 H.263是真正的格式 H.265正在測試中 avi H.264 H.263 視頻解碼&#xff1a;將H.263 H.264轉換為視頻和音頻的格式 視頻編碼&#xff1a; 解碼&#xff1a;硬解碼&#xff1a;GPU做的解碼 CPU做除了圖片以外的&#…

10.11 安裝pod

原文地址&#xff1a;http://www.jianshu.com/p/5fc15906c53a 感謝。 更新升級10.11 cocoapods安裝出問題最簡單的解決方法 這是因為10.11把cocoapods直接干掉了 sudo gem install -n /usr/local/bin cocoapods 再加一句&#xff0c;完美解決 sudo xcode-select --switch /App…

windows任務管理器中的工作設置內存,內存專用工作集,提交大小詳解

通俗的講工作設置內存是程序占用的物理內存(包含與其他程序共享的一部分), 內存專用工作集是程序獨占的物理內存, 提交大小是程序獨占的內存(包含物理內存和在頁面文件中的內存). 注:頁面文件就是存放不在物理內存中的內存,文件路徑一般在C:\pagefile.sys,目的是為了能夠讓更…

coreData mapView #include

一、coreData 需要導入系統庫CoreData 新建文件的時候選core data 中的Data Model 然后加相應的表 加完表后 在Model.xcdatamodeld中新建文件 選coredata中的第三個 即可 如果兩個表有聯系 就將附表放到主表中 在build Setting中&#xff0c;輸入search 在Framework Searc…

淺談 C++ 中的 new/delete 和 new[]/delete[]

來自&#xff1a;http://www.cnblogs.com/hazir/p/new_and_delete.html 在 C 中&#xff0c;你也許經常使用 new 和 delete 來動態申請和釋放內存&#xff0c;但你可曾想過以下問題呢&#xff1f; new 和 delete 是函數嗎&#xff1f;new [] 和 delete [] 又是什么&#xff1f…

JavaScript中的原型繼承原理

在JavaScript當中&#xff0c;對象A如果要繼承對象B的屬性和方法&#xff0c;那么只要將對象B放到對象A的原型鏈上即可。而某個對象的原型鏈&#xff0c;就是由該對象開始&#xff0c;通過__proto__屬性連接起來的一串對象。__proto__屬性是JavaScript對象中的內部屬性&#xf…

XMPP

XMPP不支持視頻聊天 支持文字聊天 但對于圖片和語音聊天支持的不好 那么就將他們轉成NSData的形式 IM Instance Message 即時聊天 聊天系統中 XMPP主要做兩個人的聊天和群聊&#xff0c;只用了這兩個功能 還有一個HTTP的服務器 &#xff0c;他是一個旁路服務器 XMPP底層…

QT 調試

QT調試&#xff08;參考下面的說明就可以正常調試&#xff09;&#xff1a;http://blog.csdn.net/wchengshen/article/details/50254731http://blog.csdn.net/sx341125/article/details/53606534 QT調用DLL&#xff1a; Qt中調用VS編譯dll的方法(一)----顯式調用 qt使用動態庫(…

建模元件有哪些在MapleSim中

信號庫&#xff1a;包含通用信號模塊、布爾、控制器、離散信號模塊、信號源、線性信號模塊、非線性信號模塊、時間離散信號模塊、查詢表、信號轉換器、數學運算、關系元件、特殊信號模塊&#xff0c;應用案例。 電子庫&#xff1a;包含電阻、運算放大器、二極管、步進電機、模擬…

iOS小筆記

controller&#xff1a;連接二者的橋梁&#xff1b;cocoa frameworks 有兩個框架&#xff1a;foundationfoundation 是cocoa中最基本的一些類&#xff1b;再mac應用程序中負責對象管理&#xff0c;內存管理&#xff0c;容器等相關數據&#xff1b;uikit&#xff1a;uikit&…

【C++】VS2010將寫好的程序打包成安裝文件發布

參考鏈接&#xff1a;http://blog.csdn.net/yongh701/article/details/51326142 我們可以將自己寫好的VS2010程序打包成安裝文件&#xff0c;給用戶安裝&#xff0c;具體步驟如下&#xff1a; 1、如下圖&#xff0c;同樣是新建一個項目&#xff0c;但是這次是新建一個其它項目…

01_jeecms建站

一、環境安裝 JDK5TOMCAT5.5MYSQL5及以上http://www.jeecms.com/tutorial/index.jhtml參考環境安裝篇二、解壓文件安裝包jeecms-v5zip,如圖圖1ROOT文件夾復制放到tomcat下的webapps文件夾&#xff08;注&#xff1a;請先刪除webapps下原有的默認ROOT文件夾&#xff09;如不想部…

WiFi基本知識

轉自&#xff1a;http://blog.csdn.net/myarrow/article/details/7930131 1. IE802.11簡介 標準號IEEE 802.11bIEEE 802.11aIEEE 802.11gIEEE 802.11n標準發布時間1999年9月1999年9月2003年6月2009年9月工作頻率范圍2.4&#xff0d;2.4835GHz 5.150&#xff0d;5.350GHz5.475&a…

iOS各種小理論知識

Objective-C 部分 1. 你如何理解 OC 的內存管理 OC 內存管理是基于引用計數。誰想使用某個對象 B,就要把對象 B 的計數器1,如果不 使用這個對象了,那么就把對象 B 計數器-1,如果 B 對象計數器減到 0,那么 B 對象自動會調用自己的 dealloc 函數,也就是這個對象被銷毀。 一…

libev 宏展開

想看源碼&#xff0c;宏太多&#xff0c;看著累&#xff0c;宏展開&#xff0c;再看&#xff0c;功力時間不夠&#xff0c;先放下 放上宏展開后的代碼。 libev4.20 展開方示為 ./configure 修改makefile文件&#xff0c;字符串 替換CC為 CPP 注意要把基礎的CC定義保留 make mv …