1. 執行緒的啟動
建立乙個使用者介面執行緒,首先要從類cwinthread產生乙個派生類,同時必須使用declare_dyncreate和implement_dyncreate來宣告和實現這個cwinthread派生類。
第二步是根據需要過載該派生類的一些成員函式如:exitinstance();initinstance();onidle();pretranslatemessage()等函式,最後啟動該使用者介面執行緒,呼叫afxbeginthread()函式的乙個版本:cwinthread* afxbeginthread( cruntimeclass* pthreadclass, int npriority = thread_priority_normal, uint nstacksize = 0, dword dwcreateflags = 0, lpsecurity_attributes lpsecurityattrs = null );其中第乙個引數為指向定義的使用者介面執行緒類指標變數,第二個引數為執行緒的優先順序,第三個引數為執行緒所對應的堆疊大小,第四個引數為執行緒建立時的附加標誌,預設為正常狀態,如為create_suspended則執行緒啟動後為掛起狀態。
對於工作執行緒來說,啟動乙個執行緒,首先需要編寫乙個希望與應用程式的其餘部分並行執行的函式如fun1(),接著定義乙個指向cwinthread物件的指標變數*pthread,呼叫afxbeginthread(fun1,param,priority)函式,返回值付給pthread變數的同時一併啟動該執行緒來執行上面的fun1()函式,其中fun1是執行緒要執行的函式的名字,也既是上面所說的控制函式的名字,param是準備傳送給執行緒函式fun1的任意32位值,priority則是定義該執行緒的優先級別,它是預定義的常數,讀者可參考msdn。
2.執行緒的優先順序
以下的cwinthread類的成員函式用於執行緒優先順序的操作:
int getthreadpriority();
bool setthradpriority()(int npriority);
上述的二個函式分別用來獲取和設定執行緒的優先順序,這裡的優先順序,是相對於該執行緒所處的優先權層次而言的,處於同一優先權層次的執行緒,優先順序高的執行緒先執行;處於不同優先權層次上的執行緒,誰的優先權層次高,誰先執行。至於優先順序設定所需的常數,自己參考msdn就可以了,要注意的是要想設定執行緒的優先順序,這個執行緒在建立時必須具有thread_set_information訪問許可權。對於執行緒的優先權層次的設定,cwinthread類沒有提供相應的函式,但是可以通過win32 sdk函式getpriorityclass()和setpriorityclass()來實現。
3.執行緒的懸掛、恢復
cwinthread類中包含了應用程式懸掛和恢復它所建立的執行緒的函式,其中suspendthread()用來懸掛執行緒,暫停執行緒的執行;resumethread()用來恢復執行緒的執行。如果你對乙個執行緒連續若干次執行suspendthread(),則需要連續執行相應次的resumethread()來恢復執行緒的執行。
4.結束執行緒
//ctestview message handlers
/set to true to end thread
bool bend=false;//定義的全域性變數,用於控制線程的執行
//the thread function
uint threadfunction(lpvoid pparam)//執行緒函式
return 0;
}cwinthread *pthread;
hwnd hwnd;
/void ctestview::oninitialupdate()
void ctestview::ondestroy()
三、 執行緒之間的通訊
通常情況下,乙個次級執行緒要為主執行緒完成某種特定型別的任務,這就隱含著表示在主線程和次級執行緒之間需要建立乙個通訊的通道。一般情況下,有下面的幾種方法實現這種通訊任務:使用全域性變數(上一節的例子其實使用的就是這種方法)、使用事件物件、使用訊息。這裡我們主要介紹後兩種方法。
1. 利用使用者定義的訊息通訊uint threadfunction(lpvoid pparam)
::postmessage(hwnd,wm_usermsg,0,0);
return 0;
}wm_usermsg訊息的響應函式為onthreadended(wparam wparam,lparam lparam)
long ctestview::onthreadended(wparam wparam,lparam lparam)
上面的例子是工作者執行緒向使用者介面執行緒傳送訊息,對於工作者執行緒,如果它的設計模式也是訊息驅動的,那麼呼叫者可以向它傳送初始化、退出、執行某種特定的處理等訊息,讓它在後台完成。在控制函式中可以直接使用::getmessage()這個sdk函式進行訊息分檢和處理,自己實現乙個訊息迴圈。getmessage()函式在判斷該執行緒的訊息隊列為空時,執行緒將系統分配給它的時間片讓給其它執行緒,不無效的占用cpu的時間,如果訊息佇列不為空,就獲取這個訊息,判斷這個訊息的內容並進行相應的處理。
2.用事件物件實現通訊
cevent threadstart,threadend;
uint threadfunction(lpvoid pparam)
::postmessage(hwnd,wm_usermsg,0,0);
return 0;}/
void ctestview::oninitialupdate()
void ctestview::ondestroy()
執行這個程式,當關閉程式時,才顯示提示框,顯示"thread ended"
四、 執行緒之間的同步
前面我們講過,各個執行緒可以訪問程序中的公共變數,所以使用多執行緒的過程中需要注意的問題是如何防止兩個或兩個以上的執行緒同時訪問同乙個資料,以免破壞資料的完整性。保證各個執行緒可以在一起適當的協調工作稱為執行緒之間的同步。前面一節介紹的事件物件實際上就是一種同步形式。visual c++中使用同步類來解決作業系統的並行性而引起的資料不安全的問題,mfc支援的七個多執行緒的同步類可以分成兩大類:同步物件(csyncobject、csemaphore、cmutex、ccriticalsection和cevent)和同步訪問物件(cmultilock和csinglelock)。本節主要介紹臨界區(critical section)、互斥(mutexe)、訊號量(semaphore),這些同步物件使各個執行緒協調工作,程式執行起來更安全。
1. 臨界區
臨界區是保證在某乙個時間只有乙個執行緒可以訪問資料的方法。使用它的過程中,需要給各個執行緒提供乙個共享的臨界區物件,無論哪個執行緒占有臨界區物件,都可以訪問受到保護的資料,這時候其它的執行緒需要等待,直到該執行緒釋放臨界區物件為止,臨界區被釋放後,另外的執行緒可以強佔這個臨界區,以便訪問共享的資料。臨界區對應著乙個ccriticalsection物件,當執行緒需要訪問保護資料時,呼叫臨界區物件的lock()成員函式;當對保護資料的操作完成之後,呼叫臨界區物件的unlock()成員函式釋放對臨界區物件的擁有權,以使另乙個執行緒可以奪取臨界區物件並訪問受保護的資料。同時啟動兩個執行緒,它們對應的函式分別為writethread()和readthread(),用以對公共陣列組array操作,下面的**說明了如何使用臨界區物件:
#include "afxmt.h"
int array[10],destarray[10];
ccriticalsection section;
uint writethread(lpvoid param)
uint readthread(lpvoid param)
上述**執行的結果應該是destarray陣列中的元素分別為1-9,而不是雜亂無章的數,如果不使用同步,則不是這個結果,有興趣的讀者可以實驗一下。
2. 互斥
互斥與臨界區很相似,但是使用時相對複雜一些,它不僅可以在同一應用程式的執行緒間實現同步,還可以在不同的程序間實現同步,從而實現資源的安全共享。互斥與cmutex類的物件相對應,使用互斥物件時,必須建立乙個csinglelock或cmultilock物件,用於實際的訪問控制,因為這裡的例子只處理單個互斥,所以我們可以使用csinglelock物件,該物件的lock()函式用於占有互斥,unlock()用於釋放互斥。實現**如下:
#include "afxmt.h"
int array[10],destarray[10];
cmutex section;
/uint writethread(lpvoid param)
uint readthread(lpvoid param)
3. 訊號量
訊號量的用法和互斥的用法很相似,不同的是它可以同一時刻允許多個執行緒訪問同乙個資源,建立乙個訊號量需要用csemaphore類宣告乙個物件,一旦建立了乙個訊號量物件,就可以用它來對資源的訪問技術。要實現計數處理,先建立乙個csinglelock或cmltilock物件,然後用該物件的lock()函式減少這個訊號量的計數值,unlock()反之。下面的**分別啟動三個執行緒,執行時同時顯示二個訊息框,然後10秒後第三個訊息框才得以顯示。
/csemaphore *semaphore;
semaphore=new csemaphore(2,2);
hwnd hwnd=getsafehwnd();
afxbeginthread(threadproc1,hwnd);
afxbeginthread(threadproc2,hwnd);
afxbeginthread(threadproc3,hwnd);
//uint threadproc1(lpvoid param)
uint threadproc2(lpvoid param)
uint threadproc3(lpvoid param)
對複雜的應用程式來說,執行緒的應用給應用程式提供了高效、快速、安全的資料處理能力。本文講述了執行緒中經常遇到的問題,希望對讀者朋友有一定的幫助。
Windows多執行緒多工設計初步
1 執行緒的啟動 建立乙個使用者介面執行緒,首先要從類cwinthread產生乙個派生類,同時必須使用declare dyncreate和implement dyncreate來宣告和實現這個cwinthread派生類。第二步是根據需要過載該派生類的一些成員函式如 exitinstance init...
多工和多執行緒 1
建立乙個執行緒的api函式叫做createthread hthread createthread security attributee,dwstacksize,threadproc,pparam,dwflags,idthread 大部分的windows程式設計師都會傾向於使用c的執行時庫函式 be...
Shell 實現多執行緒(多工)
bin bash all num 10 a date h m s for num in seq1 do done b date h m s echo e starttime t a echo e endtime t b 在命令的末尾加 符號,則命令將在後台執行,這樣後面的命令不需要等待該命令執行完再...