?? threads.html
字號:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- /home/reggie/tmp/qt-3.0-reggie-5401/qt-x11-commercial-3.0.5/doc/threads.doc:36 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="Translator" content="Cavendish">
<meta name="Qt zh_CN Documents Website" content="http://www.qiliang.net/qt">
<title>Qt中的線程支持</title>
<style type="text/css"><!--
h3.fn,span.fn { margin-left: 1cm; text-indent: -1cm; }
a:link { color: #004faf; text-decoration: none }
a:visited { color: #672967; text-decoration: none }
body { background: #ffffff; color: black; font-family: "Times New Roman" }
--></style>
</head>
<body>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr bgcolor="#E5E5E5">
<td valign=center>
<a href="index.html">
<font color="#004faf">主頁</font></a>
| <a href="classes.html">
<font color="#004faf">所有的類</font></a>
| <a href="mainclasses.html">
<font color="#004faf">主要的類</font></a>
| <a href="annotated.html">
<font color="#004faf">注釋的類</font></a>
| <a href="groups.html">
<font color="#004faf">分組的類</font></a>
| <a href="functions.html">
<font color="#004faf">函數</font></a>
</td>
<td align="right" valign="center"><img src="logo32.png" align="right" width="64" height="32" border="0"></td></tr></table>
<h1 align=center>Qt中的線程支持</h1>
<p> Qt對線程提供了支持,基本形式有獨立于平臺的線程類、線程安全方式的事件傳遞和一個全局Qt庫互斥量允許你可以從不同的線程調用Qt方法。
<p> 這個文檔是提供給那些對多線程編程有豐富的知識和經驗的聽眾的。推薦閱讀:
<ul>
<li> <a href="http://www.amazon.com/exec/obidos/ASIN/0134436989/trolltech/t">Threads Primer: A Guide to Multithreaded Programming</a>
<li> <a href="http://www.amazon.com/exec/obidos/ASIN/0131900676/trolltech/t">Thread Time: The Multithreaded Programming Guide</a>
<li> <a href="http://www.amazon.com/exec/obidos/ASIN/1565921151/trolltech/t">Pthreads Programming: A POSIX Standard for Better Multiprocessing (O'Reilly Nutshell)</a>
<li> <a href="http://www.amazon.com/exec/obidos/ASIN/1565922964/trolltech/t">Win32 Multithreaded Programming</a>
</ul>
<p> <b>警告:</b>所有的GUI類(比如,<a href="qwidget.html">QWidget</a>和它的子類),操作系統核心類(比如,<a href="qprocess.html">QProcess</a>)和網絡類都<em>不</em>是線程安全的。
<p> <a href="qregexp.html">QRegExp</a>使用一個靜態緩存并且也不是線程安全的,即使通過使用<a href="qmutex.html">QMutex</a>來保護的QRegExp對象。
<p>
<p> <h2> 啟用線程支持
</h2>
<a name="1"></a><p> 在Windows上安裝Qt時,在一些編譯器上線程支持是一個選項。
<p> 在Mac OS X和Unix上,線程支持可以當你在運行<tt>configure</tt>腳本時添加<tt>-thread</tt>選項就可以生效了。在Unix平臺上,多線程程序必須用特殊的方式連接,比如使用特殊的libc,安裝程序將會創建另外一個庫<tt>libqt-mt</tt>并且因此線程程序必須和這個庫進行連接(使用<tt>-lqt-mt</tt>)而不是標準的Qt庫。
<p> 在兩個平臺上,你都應該定義宏<tt>QT_THREAD_SUPPORT</tt>來編譯(比如,編譯時使用<tt>-DQT_THREAD_SUPPORT</tt>)。在Windows上,這個通常可以在<tt>qconfig.h</tt>寫一個條目來解決。
<p> <h2> 線程類
</h2>
<a name="2"></a><p> 最重要的類是<a href="qthread.html">QThread</a>,也就是說要開始一個新的線程,就是開始執行你重新實現的<a href="qthread.html#run">QThread::run</a>()。這和Java的線程類很相似。
<p> 為了寫線程程序,在兩個線程同時希望訪問同一個數據時,對數據進行保護是很必要的。因此這里也有一個<a href="qmutex.html">QMutex</a>類,一個線程可以鎖定互斥量,并且在它鎖定之后,其它線程就不能再鎖定這個互斥量了,試圖這樣做的線程都會被阻塞直到互斥量被釋放。例如:
<p> <pre>
class MyClass
{
public:
void doStuff( int );
private:
<a href="qmutex.html">QMutex</a> mutex;
int a;
int b;
};
// 這里設置a為c,b為c*2。
void MyClass::doStuff( int c )
{
mutex.<a href="qmutex.html#lock">lock</a>();
a = c;
b = c * 2;
mutex.<a href="qmutex.html#unlock">unlock</a>();
}
</pre>
<p> 這保證了同一時間只有一個線程可以進入MyClass::doStuff(),所以<tt>b</tt>將永遠等于<tt>c * 2</tt>。
<p> 另外一個線程也需要在一個給定的條件下等待其它線程的喚醒,<a href="qwaitcondition.html">QWaitCondition</a>類就被提供了。線程等待的條件QWaitCondition指出發生了什么事情,阻塞將一直持續到這種事情發生。當某種事情發生了,<a href="qwaitcondition.html">QWaitCondition</a>可以喚醒等待這一事件的線程之一或全部。(這和POSIX線程條件變量是具有相同功能的并且它也是Unix上的一種實現。)例如:
<p> <pre>
#include <<a href="qapplication-h.html">qapplication.h</a>>
#include <<a href="qpushbutton-h.html">qpushbutton.h</a>>
// 全局條件變量
<a href="qwaitcondition.html">QWaitCondition</a> mycond;
// Worker類實現
class Worker : public <a href="qpushbutton.html">QPushButton</a>, public QThread
{
<a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>
public:
Worker(QWidget *parent = 0, const char *name = 0)
: <a href="qpushbutton.html">QPushButton</a>(parent, name)
{
setText("Start Working");
// 連接從QPushButton繼承來的信號和我們的slotClicked()方法
connect(this, SIGNAL(clicked()), SLOT(slotClicked()));
// 調用從QThread繼承來的start()方法……這將立即開始線程的執行
QThread::<a href="qthread.html#start">start</a>();
}
public slots:
void slotClicked()
{
// 喚醒等待這個條件變量的一個線程
mycond.<a href="qwaitcondition.html#wakeOne">wakeOne</a>();
}
protected:
void run()
{
// 這個方法將被新創建的線程調用……
while ( TRUE ) {
// 鎖定應用程序互斥鎖,并且設置窗口標題來表明我們正在等待開始工作
qApp-><a href="qapplication.html#lock">lock</a>();
setCaption( "Waiting" );
qApp-><a href="qapplication.html#unlock">unlock</a>();
// 等待直到我們被告知可以繼續
mycond.<a href="qwaitcondition.html#wait">wait</a>();
// 如果我們到了這里,我們已經被另一個線程喚醒……讓我們來設置標題來表明我們正在工作
qApp-><a href="qapplication.html#lock">lock</a>();
setCaption( "Working!" );
qApp-><a href="qapplication.html#unlock">unlock</a>();
// 這可能會占用一些時間,幾秒、幾分鐘或者幾小時等等,因為這個一個和GUI線程分開的線程,在處理事件時,GUI線程不會停下來……
do_complicated_thing();
}
}
};
// 主線程——所有的GUI事件都由這個線程處理。
int main( int argc, char **argv )
{
<a href="qapplication.html">QApplication</a> app( argc, argv );
// 創建一個worker……當我們這樣做的時候,這個worker將在一個線程中運行
Worker firstworker( 0, "worker" );
app.<a href="qapplication.html#setMainWidget">setMainWidget</a>( &worker );
worker.show();
return app.<a href="qapplication.html#exec">exec</a>();
}
</pre>
<p> 只要你按下按鈕,這個程序就會喚醒worker線程,這個線程將會進行并且做一些工作并且然后會回來繼續等待被告知做更多的工作。如果當按鈕被按下時,worker線程正在工作,那么就什么也不會發生。當線程完成了工作并且再次調用<a href="qwaitcondition.html#wait">QWaitCondition::wait</a>(),然后它就會被開始。
<p> <h2> 線程安全的事件傳遞
</h2>
<a name="3"></a><p> 在Qt中,一個線程總是一個事件線程——確實是這樣的,線程從窗口系統中拉出事件并且把它們分發給窗口部件。靜態方法QThread::postEvent從線程中傳遞事件,而不同于事件線程。事件線程被喚醒并且事件就像一個普通窗口系統事件那樣在事件線程中被分發。例如,你可以強制一個窗口部件通過如下這樣做的一個不同的線程來進行重繪:
<p> <pre>
<a href="qwidget.html">QWidget</a> *mywidget;
QThread::<a href="qthread.html#postEvent">postEvent</a>( mywidget, new <a href="qpaintevent.html">QPaintEvent</a>( QRect(0, 0, 100, 100) ) );
</pre>
<p> 這(異步地)將使mywidget重繪一塊100*100的正方形區域。
<p> <h2> Qt庫互斥量
</h2>
<a name="4"></a><p> Qt庫互斥量提供了從線程而不是事件線程中調用Qt方法的一種方法。例如:
<p> <pre>
<a href="qapplication.html">QApplication</a> *qApp;
<a href="qwidget.html">QWidget</a> *mywidget;
qApp-><a href="qapplication.html#lock">lock</a>();
mywidget-><a href="qwidget.html#setGeometry">setGeometry</a>(0,0,100,100);
<a href="qpainter.html">QPainter</a> p;
p.<a href="qpainter.html#begin">begin</a>(mywidget);
p.<a href="qpainter.html#drawLine">drawLine</a>(0,0,100,100);
p.<a href="qpainter.html#end">end</a>();
qApp-><a href="qapplication.html#unlock">unlock</a>();
</pre>
<p> 在Qt中沒有使用互斥量而調用一個函數通常情況下結果將是不可預知的。從另外一個線程中調用Qt的一個GUI相關函數需要使用Qt庫互斥量。在這種情況下,所有可能最終訪問任何圖形或者窗口系統資源的都是GUI相關的。使用容器類,字符串或者輸入/輸出類,如果對象只被一個線程使用就不需要任何互斥量了。
<p> <h2> 告誡
</h2>
<a name="5"></a><p> 當進行線程編程時,需要注意的一些事情:
<ul>
<li> 當使用Qt庫互斥量的時候不要做任何阻塞操作。這將會凍結事件循環。
<p> <li> 確認你鎖定一個遞歸<a href="qmutex.html">QMutex</a>的次數和解鎖的次數一樣,不能多也不能少。
<p> <li> 在調用除了Qt容器和工具類的任何東西之前鎖定Qt應用程序互斥量。
<p> <li> 謹防<a href="shclass.html#implicitly-shared">隱含地共享</a>類,你應該避免在線程之間使用操作符=()來復制它們。這將會在Qt的未來主要的或次要的發行版本中進行改進。
<p> <li> 謹防那些沒有被設計為線程安全的Qt類,例如,<a href="qptrlist.html">QPtrList</a>的應用程序接口就不是線程安全的并且如果不同的線程需要遍歷一個QPtrList,它們應該在調用<a href="qptrlist.html#first">QPtrList::first</a>()之前鎖定并且在到達終點之后解鎖,而不是在<a href="qptrlist.html#next">QPtrList::next</a>()的前后進行鎖定和解鎖。
<p> <li> 確認只在GUI線程中創建的繼承和使用了<a href="qwidget.html">QWidget</a>、<a href="qtimer.html">QTimer</a>和<a href="qsocketnotifier.html">QSocketNotifier</a>的對象。在一些平臺上,在某個不是GUI線程的線程中創建這樣的對象將永遠不會接受到底層窗口系統的事件。
<p> <li> 和上面很相似,只在GUI線程中使用QNetwork類。一個經常被問到的問題是一個<a href="qsocket.html">QSocket</a>是否可以在多線程中使用。這不是必須得,因為所有的QNetwork類都是異步的。
<p> <li> 不要在不是GUI線程的線程中試圖調用processEvents()函數。這也包括<a href="qdialog.html#exec">QDialog::exec</a>()、<a href="qpopupmenu.html#exec">QPopupMenu::exec</a>()、<a href="qapplication.html#processEvents">QApplication::processEvents</a>()和其它一些。
<p> <li> 在你的應用程序中,不要把普通的Qt庫和支持線程的Qt庫混合使用。這也就是說如果你的程序使用了支持線程的Qt庫,你就不應該連接普通的Qt庫、動態的載入普通Qt庫或者動態地連接其它依賴普通Qt庫的庫或者插件。在一些系統上,這樣做會導致Qt庫中使用的靜態數據變得不可靠了。
<p> </ul>
<p>
<!-- eof -->
<p><address><hr><div align=center>
<table width=100% cellspacing=0 border=0><tr>
<td>Copyright © 2002
<a href="http://www.trolltech.com">Trolltech</a>
<td><a href="http://www.trolltech.com/trademarks.html">Trademarks</a>
<td><a href="zh_CN.html">譯者:Cavendish</a>
<td align=right><div align=right>Qt 3.0.5版</div>
</table></div></address></body>
</html>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -