[Qt教程] 第39篇 網絡(九)進程和線程
樓主
發表於 2013-8-29 15:48:56 | 查看:
415| 回複: 0

進程和線程
版權聲明
該文章原創於作者yafeilinux,轉載請注明出處!
導語
在前麵的幾節內容中講解了Qt網絡編程的一些基本內容,這一節來看一下在Qt中進程和線程的基本應用。
環境:Windows
Xp + Qt 4.8.5+Qt Creator2.8.0
目錄
一、進程
二、線程
正文
一、進程
在設計一個應用程序時,有時不希望將一個不太相關的功能集成到程序中,或者是因為該功能與當前設計的應用程序聯係不大,或者是因為該功能已經可以使用現成的程序很好的實現了,這時就可以在當前的應用程序中調用外部的程序來實現該功能,這就會使用到進程。Qt應用程序可以很容易的啟動一個外部應用程序,而且Qt也提供了在多種進程間通信的方法。
Qt的QProcess類用來啟動一個外部程序並與其進行通信。下麵我們來看一下怎麼在Qt代碼中啟動一個進程。
1.首先創建QtGui應用。
工程名稱為“myProcess”,其他選項保持默認即可。
2.然後設計界麵。
在設計模式往界麵上拖入一個Push Button部件,修改其顯示文本為“啟動一個進程”。
3.修改槽。
在按鈕上點擊鼠標右鍵,轉到其clicked()信號對應的槽,更改如下:
void MainWindow::on_pushButton_clicked()
{
myProcess.start("notepad.exe");
}
4.進入mainwindow.h文件添加代碼。
先添加頭文件包含:#include <QProcess>,然後添加私有對象定義:QProcess myProcess;
5.運行程序。
當單擊界麵上的按鈕時就會彈出一個記事本程序。
這裏我們使用QProcess對象運行了Windows係統下的記事本程序(即notepad.exe程序),因為該程序在係統目錄中,所以這裏不需要指定其路徑。大家也可以運行其他任何的程序,隻需要指定其具體路徑即可。我們看到,可以使用start()來啟動一個程序,有時啟動一個程序時需要指定啟動參數,這種情況在命令行啟動程序時是很常見的,下麵來看一個例子,還在前麵的例子的基礎上進行更改。
1.在mainwindow.h文件中添加代碼。
添加私有槽:
private slots:
void showResult();
2.在mainwindow.cpp文件中添加代碼。
(1)先添加頭文件包含:#include <QDebug>,然後在構造函數中添加如下代碼:
connect(&myProcess,SIGNAL(readyRead()), this, SLOT(showResult()));
(2)然後添加showResult()槽的定義:
void MainWindow::showResult()
{
qDebug()
<< "showResult: " << endl
<< QString(myProcess.readAll());
}
(3)最後將前麵按鈕的單擊信號對應的槽更改為:
void MainWindow::on_pushButton_clicked()
{
QString program = "cmd.exe";
QStringList arguments;
arguments << "/c dir&pause";
myProcess.start(program,
arguments);
}
這裏在啟動Windows下的命令行提示符程序cmd.exe時為其提供了命令作為參數,這樣可以直接執行該命令。當命令執行完以後可以執行showResult()槽來顯示運行的結果。這裏為了可以顯示結果中的中文字符,使用了QString()進行編碼轉換。這需要在mian()函數中添加代碼。
3. 為了確保可以顯示輸出的中文字符,在main.cpp文件中添加代碼。
先添加頭文件包含#include <QTextCodec>,然後在main()函數第一行代碼下麵,添加如下一行代碼:
QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
4.運行程序。
按下界麵上的按鈕,會在Qt Creator中的應用程序輸出欄中輸出命令的執行結果。
對於Qt中進程進一步的使用可以參考QProcess類的幫助文檔。在Qt中還提供了多種進程間通信的方法,大家可以在Qt幫助中查看Inter-ProcessCommunication
in Qt關鍵字對應的文檔。
二、線程
Qt提供了對線程的支持,這包括一組與平台無關的線程類,一個線程安全的發送事件的方式,以及跨線程的信號-槽的關聯。這些使得可以很容易的開發可移植的多線程Qt應用程序,可以充分利用多處理器的機器。多線程編程也可以有效的解決在不凍結一個應用程序的用戶界麵的情況下執行一個耗時的操作的問題。關於線程的內容,大家可以在Qt幫助中參考Thread
Support in Qt關鍵字。
(一)啟動一個線程
Qt中的QThread類提供了平台無關的線程。一個QThread代表了一個在應用程序中可以獨立控製的線程,它與進程中的其他線程分享數據,但是是獨立執行的。相對於一般的程序都是從main()函數開始執行,QThread從run()函數開始執行。默認的,run()通過調用exec()來開啟事件循環。要創建一個線程,需要子類化QThread並且重新實現run()函數。
每一個線程可以有自己的事件循環,可以通過調用exec()函數來啟動事件循環,可以通過調用exit()或者quit()來停止事件循環。在一個線程中擁有一個事件循環,可以使它能夠關聯其他線程中的信號到本線程的槽上,這使用了隊列關聯機製,就是在使用connect()函數進行信號和槽的關聯時,將Qt::ConnectionType類型的參數指定為Qt::QueuedConnection。擁有事件循環還可以使該線程能過使用需要事件循環的類,比如QTimer和QTcpSocket類等。注意,在線程中是無法使用任何的部件類的。
下麵來看一個在圖形界麵程序中啟動一個線程的例子,在界麵上有兩個按鈕,一個用於開啟一個線程,一個用於關閉該線程。
1.創建項目。
新建Qt Gui應用,名稱為“myThread”,類名為“Dialog”,基類選擇QDialog。
2.設計界麵。
完成項目創建後進入設計模式,向界麵中放入兩個Push Button按鈕,將第一個按鈕的顯示文本更改為“啟動線程”,將其objectName屬性更改為startButton;將第二個按鈕的顯示文本更改為“終止線程”,將其objectName屬性更改為stopButton,將其enabled屬性取消選中。
3.添加自定義線程類。
向項目中添加新的C++類,類名設置為“MyThread”,基類設置為“QThread”,類型信息選擇“繼承自QObject”。完成後進入mythread.h文件,先添加一個公有函數聲明:
void stop();
然後再添加一個函數聲明和一個變量的定義:
protected:
void run();
private:
volatile bool stopped;
這裏stopped變量使用了volatile關鍵字,這樣可以使它在任何時候都保持最新的值,從而可以避免在多個線程中訪問它時出錯。然後進入mythread.cpp文件中,先添加頭文件#include
<QDebug>,然後在構造函數中添加如下代碼:
stopped = false;
這裏將stopped變量初始化為false。下麵添加run()函數的定義:
void MyThread::run()
{
qreal i = 0;
while (!stopped)
qDebug() << QString("in MyThread: %1").arg(i++);
stopped = false;
}
這裏一直判斷stopped變量的值,隻要它為false,那麼就一直打印字符串。下麵添加stop()函數的定義:
void MyThread::stop()
{
stopped = true;
}
在stop()函數中將stopped變量設置為了true,這樣便可以結束run()函數中的循環,從而從run()函數中退出,這樣整個線程也就結束了。這裏使用了stopped變量來實現了進程的終止,並沒有使用危險的terminate()函數。
4.在Dialog類中使用自定義的線程。
先到dialog.h文件中,添加頭文件包含:
#include "mythread.h"
然後添加私有對象的定義:
MyThread thread;
下麵到設計模式,分別進入兩個按鈕的單擊信號對應的槽,更改如下:
// 啟動線程按鈕
void Dialog::on_startButton_clicked()
{
thread.start();
ui->startButton->setEnabled(false);
ui->stopButton->setEnabled(true);
}
// 終止線程按鈕
void Dialog::on_stopButton_clicked()
{
if (thread.isRunning()) {
thread.stop();
ui->startButton->setEnabled(true);
ui->stopButton->setEnabled(false);
}
}
在啟動線程時調用了start()函數,然後設置了兩個按鈕的狀態。在終止線程時,先使用isRunning()來判斷線程是否在運行,如果是,則調用stop()函數來終止線程,並且更改兩個按鈕的狀態。現在運行程序,按下“啟動線程”按鈕,查看應用程序輸出欄的輸出,然後再按下“終止線程”按鈕,可以看到已經停止輸出了。
下麵我們接著來優化這個程序,通過信號和槽來將子線程中的字符串顯示到主界麵上。
1.在mythread.h文件中添加信號的定義:
signals:
void stringChanged(const QString &);
2.然後到mythread.cpp文件中更改run()函數的定義:
void MyThread::run()
{
long int i = 0;
while (!stopped) {
QString str = QString("in MyThread: %1").arg(i);
emit stringChanged(str);
msleep(1000);
i++;
}
stopped = false;
}
這裏每隔1秒就發射一次信號,裏麵包含了生成的字符串。
3.到dialog.h文件中添加槽聲明:
private slots:
void changeString(const QString &);
4.打開dialog.ui,然後向主界麵上拖入一個Label標簽部件。
5.到dialog.cpp文件中,在構造函數裏麵添加信號和槽的關聯:
// 關聯線程中的信號和本類中的槽
connect(&thread, SIGNAL(stringChanged(QString)),
this, SLOT(changeString(QString)));
6.然後添加槽的定義:
void Dialog::changeString(const QString &str)
{
ui->label->setText(str);
}
這裏就是將子線程發送過來的字符串顯示到主界麵上。現在可以運行程序,查看效果了。
(二)線程同步
Qt中的QMutex、QReadWriteLock、QSemaphore和QWaitCondition類提供了同步線程的方法。雖然使用線程的思想是多個線程可以盡可能的並發執行,但是總有一些時刻,一些線程必須停止來等待其他線程。例如,如果兩個線程嚐試同時訪問相同的全局變量,結果通常是不確定的。QMutex提供了一個互斥鎖(mutex);QReadWriteLock即讀-寫鎖;QSemaphore即信號量;QWaitCondition即條件變量。
(三)可重入與線程安全
在查看Qt的幫助文檔時,在很多類的開始都寫著“All functions in this class are reentrant”,或者“All
functions in this class are thread-safe”。在Qt文檔中,術語“可重入(reentrant)”和“線程安全(thread-safe)”用來標記類和函數,來表明怎樣在多線程應用程序中使用它們:
結語
最後要注意的是,使用線程是很容易出現問題的,比如無法在主線程以外的線程中使用GUI類的問題(可以簡單的通過這樣的方式來解決:將一些非常耗時的操作放在一個單獨的工作線程中來進行,等該工作線程完成後將結果返回給主線程,最後由主線程將結果顯示到屏幕上)。大家應該謹慎的使用線程。
涉及到的源碼下載: ![]() |
最後更新:2017-04-03 14:54:11
上一篇:
[Qt教程] 第40篇 網絡(十)WebKit初識
下一篇:
[Qt教程] 第38篇 網絡(八)TCP(二)
Storm之Bolt-接口
你真的了解html嗎?--之script標簽介紹
業務實時監控服務(ARMS)征文活動
javascript中encodeURI和decodeURI方法
商業派微軟的另一麵
較實用的android Gallery Demo 網絡讀取圖片,定時切換,正反無限循環,圖片滿屏,遮罩標題文字,切換圓點顯示,多線程控製
連載:麵向對象葵花寶典:思想、技巧與實踐(26) - 類模型三板斧
中文機器名引起BEA ALBPM,IBM DB2之水土不服?
Linux服務器管理控製麵板wdcp安全設置,讓你的後台,隻有你自己能訪問!
阿裏雲聯合思科、中標軟件推商業軟件免費試用計劃