線程
l??????? 窗口置頂
::SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | WS_EX_TOPMOST);
l??????? 取消置頂
::SetWindowPos(GetSafeHwnd(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
?
l??????? 寫入配置文件。
::WritePrivateProfileString("Settings","AlwaysOnTop",m_bAlwaysOnTop? "1":"0",".\Settings.ini");
l??????? 讀取配置文件
::GetPrivateProfileInt("Selections","m_bAddNew",0,".\Settings.ini")
?
l??????? 數(shù)據(jù)綁定
DDX_Check(pDX, IDC_CHECK1_ADDNEW, m_bAddNew);
?
//讀寫INI/ini文件*************************************************?
//寫入值字段名變量名值帶目錄文件名?
WritePrivateProfileString("目錄","path",cs, regpath);?
//寫入結(jié)構(gòu)體字段名變量名值大小帶目錄文件名?
WritePrivateProfileStruct("字體","font",&LF,sizeof(LOGFONT),regpath);//結(jié)構(gòu)體?
//讀入字符字段名變量名默認(rèn)值字符緩沖區(qū)長度帶目錄文件名?
GetPrivateProfileString("目錄","path","", buffer.GetBuffer(260),260,regpath);?
//讀入整數(shù)值字段名變量名默認(rèn)值帶目錄文件名?
GetPrivateProfileInt("colors","red",255, regpath);?
//讀入結(jié)構(gòu)體字段名變量名值大小帶目錄文件名?
GetPrivateProfileStruct("字體","font",&LF,sizeof(LOGFONT),regpath);?
線程
進(jìn)程是資源分配的基本單位。所有與該進(jìn)程有關(guān)的資源,都被記錄在進(jìn)程控制塊PCB中。以表示該進(jìn)程擁有這些資源或正在使用它們?! ?/p>
另外,進(jìn)程也是搶占處理機(jī)的調(diào)度單位,它擁有一個(gè)完整的虛擬地址空間。
與進(jìn)程相對應(yīng),線程與資源分配無關(guān),它屬于某一個(gè)進(jìn)程,并與進(jìn)程內(nèi)的其他線程一起共享進(jìn)程的資源?! ?/p>
當(dāng)進(jìn)程發(fā)生調(diào)度時(shí),不同的進(jìn)程擁有不同的虛擬地址空間,而同一進(jìn)程內(nèi)的不同線程共享同一地址空間。
線程只由相關(guān)堆棧(系統(tǒng)棧或用戶棧)寄存器和線程控制表TCB組成。寄存器可被用來存儲(chǔ)線程內(nèi)的局部變量,但不能存儲(chǔ)其他線程的相關(guān)變量。
發(fā)生進(jìn)程切換與發(fā)生線程切換時(shí)相比較,進(jìn)程切換時(shí)涉及到有關(guān)資源指針的保存以及地址空間的變化等問題;線程切換時(shí),由于同進(jìn)程內(nèi)的線程共享資源和地址空間,將不涉及資源信息的保存和地址變化問題,從而減少了操作系統(tǒng)的開銷時(shí)間。而且,進(jìn)程的調(diào)度與切換都是由操作系統(tǒng)內(nèi)核完成,而線程則既可由操作系統(tǒng)內(nèi) 核完成,也可由用戶程序進(jìn)行。
因?yàn)檫M(jìn)程內(nèi)的線程分享相同的資源,所以需要在系統(tǒng)級(jí)別上設(shè)置控制機(jī)制來保證數(shù)據(jù)的完整性。當(dāng)一個(gè)線程修改了某個(gè)變量,而另一個(gè)線程試圖讀取它時(shí),或者兩個(gè)線程同時(shí)修改同一變量,就會(huì)影響到數(shù)據(jù)的完整性,為防止這個(gè)問題,操作系統(tǒng)提供了一種相互排斥對象,簡寫為mutex。在多線程程序中,mutex是通過編程來實(shí)現(xiàn)的,可防止多個(gè)線程在同一時(shí)間訪問同一資源。??? 當(dāng)一個(gè)線程需要訪問某一資源時(shí),它必須先請求一個(gè)mutex,一旦線程得到一個(gè)mutex,其他想獲取同一mutex的線程被阻塞,并處于低CPU占用的等待狀態(tài);一旦這個(gè)線程完成了數(shù)據(jù)訪問,它會(huì)釋放對應(yīng)的mutex,這就允許其他線程獲取以訪問相關(guān)的數(shù)據(jù)。
線程,有時(shí)被稱為輕量級(jí)進(jìn)程(Lightweight Process,LWP),是程序執(zhí)行流的最小單元。一個(gè)標(biāo)準(zhǔn)的線程由線程ID,當(dāng)前指令指針(PC),寄存器集合和堆棧組成。另外,線程是進(jìn)程中的一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源,但它可與同屬一個(gè)進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源。一個(gè)線程可以創(chuàng)建和撤消另一個(gè)線程,同一進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行。由于線程之間的相互制約,致使線程在運(yùn)行中呈現(xiàn)出間斷性。線程也有就緒、阻塞和運(yùn)行三種基本狀態(tài)。每一個(gè)程序都至少有一個(gè)線程,若程序只有一個(gè)線程,那就是程序本身。
線程是程序中一個(gè)單一的順序控制流程。在單個(gè)程序中同時(shí)運(yùn)行多個(gè)線程完成不同的工作,稱為多線程。???
每個(gè)線程中都應(yīng)具有一個(gè)用于控制線程運(yùn)行的線程控制塊TCB,用于指示被執(zhí)行指令序列的程序計(jì)數(shù)器、保留局部變量、少數(shù)狀態(tài)參數(shù)和返回地址等的一組寄存器和堆棧。
1.輕型實(shí)體
線程中的實(shí)體基本上不擁有系統(tǒng)資源,只是有一點(diǎn)必不可少的、能保證獨(dú)立運(yùn)行的資源,比如,在每個(gè)線程中都應(yīng)具有一個(gè)用于控制線程運(yùn)行的線程控制塊TCB,用于指示被執(zhí)行指令序列的程序計(jì)數(shù)器、保留局部變量、少數(shù)狀態(tài)參數(shù)和返回地址等的一組寄存器和堆棧。
2.獨(dú)立調(diào)度和分派的基本單位。
在多線程OS中,線程是能獨(dú)立運(yùn)行的基本單位,因而也是獨(dú)立調(diào)度和分派的基本單位。由于線程很“輕”,故線程的切換非常迅速且開銷小。
3.可并發(fā)執(zhí)行。
在一個(gè)進(jìn)程中的多個(gè)線程之間,可以并發(fā)執(zhí)行,甚至允許在一個(gè)進(jìn)程中所有線程都能并發(fā)執(zhí)行;同樣,不同進(jìn)程中的線程也能并發(fā)執(zhí)行。
4.共享進(jìn)程資源。
在同一進(jìn)程中的各個(gè)線程,都可以共享該進(jìn)程所擁有的資源,這首先表現(xiàn)在:所有線程都具有相同的地址空間(進(jìn)程的地址空間),這意味著,線程可以訪問該地址空間的每一個(gè)虛地址;此外,還可以訪問進(jìn)程所擁有的已打開文件、定時(shí)器、信號(hào)量機(jī)構(gòu)等。
線程與進(jìn)程的區(qū)別可以歸納為以下幾點(diǎn):
l???????? 地址空間和其它資源(如打開文件):進(jìn)程間相互獨(dú)立,同一進(jìn)程的各線程間共享。某進(jìn)程內(nèi)的線程在其它進(jìn)程不可見?! ?/p>
l???????? 通信:進(jìn)程間通信IPC,線程間可以直接讀寫進(jìn)程數(shù)據(jù)段(如全局變量)來進(jìn)行通信——需要進(jìn)程同步和互斥手段的輔助,以保證數(shù)據(jù)的一致性。
l???????? 調(diào)度和切換:線程上下文切換比進(jìn)程上下文切換要快得多?! ?/p>
l???????? 在多線程OS中,進(jìn)程不是一個(gè)可執(zhí)行的實(shí)體。
?
線程的生命狀態(tài)與周期
1.新建 2.就緒 3.運(yùn)行 4.阻塞 5.死亡
線程有兩個(gè)基本類型:
用戶級(jí)線程:管理過程全部由用戶程序完成,操作系統(tǒng)內(nèi)核心只對進(jìn)程進(jìn)行管理。
系統(tǒng)級(jí)線程(核心級(jí)線程):由操作系統(tǒng)內(nèi)核進(jìn)行管理。操作系統(tǒng)內(nèi)核給應(yīng)用程序提供相應(yīng)的系統(tǒng)調(diào)用和應(yīng)用程序接口API,以使用戶程序可以創(chuàng)建、執(zhí)行、撤消線程。
?
?
函數(shù)
操作系統(tǒng)
描述
使用的類
CreateThread
Windows
創(chuàng)建一個(gè)Windows線程
CThread
WaitForSingleObject
Windows
等待一個(gè)對象有信號(hào)
CThread, CMutex, CEvent
CreateMutex
Windows
創(chuàng)建一個(gè)命名或未命名的mutex
CMutex
CloseHandle
Windows
關(guān)閉某一Windows句柄以釋放資源
CMutex, CEvent, CThread
ReleaseMutex
Windows
釋放某一之前獲取,并由WaitForSingleObject鎖定的mutex
CMutex CEvent
CreateEvent
Windows
創(chuàng)建一個(gè)Windows事件對象
CEvent
SetEvent
Windows
設(shè)置某一Windows事件對象為有信號(hào)狀態(tài)
CEvent
?
?
CMutex類的函數(shù)
函數(shù)
描述
void CMutex()
構(gòu)造函數(shù)
void Lock()
鎖定mutex對象或當(dāng)它阻塞時(shí)等待
void Unlock()
解鎖之前阻塞的mutex
?
2、DWORD SuspendThread(HANDLE hThread);
該函數(shù)用于掛起指定的線程,如果函數(shù)執(zhí)行成功,則線程的執(zhí)行被終止。 3、DWORD
3、ResumeThread(HANDLE hThread);
該函數(shù)用于結(jié)束線程的掛起狀態(tài),執(zhí)行線程。
4、VOID ExitThread(DWORD dwExitCode);
該函數(shù)用于線程終結(jié)自身的執(zhí)行,主要在線程的執(zhí)行函數(shù)中被調(diào)用。其中參數(shù)dwExitCode用來設(shè)置線程的退出碼。
5、BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);
一般情況下,線程運(yùn)行結(jié)束之后,線程函數(shù)正常返回,但是應(yīng)用程序可以調(diào)用TerminateThread強(qiáng)行終止某一線程的執(zhí)行。各參數(shù)含義如下:
hThread:將被終結(jié)的線程的句柄;
dwExitCode:用于指定線程的退出碼。
使用TerminateThread()終止某個(gè)線程的執(zhí)行是不安全的,可能會(huì)引起系統(tǒng)不穩(wěn)定;雖然該函數(shù)立即終止線程的執(zhí)行,但并不釋放線程所占用的資源。因此,一般不建議使用該函數(shù)。
6、BOOL PostThreadMessage(DWORD idThread,
??????????? UINT Msg,
??????????? WPARAM wParam,
??????????? LPARAM lParam);
?
l???????? 該函數(shù)將一條消息放入到指定線程的消息隊(duì)列中,并且不等到消息被該線程處理時(shí)便返回。
l???????? idThread:將接收消息的線程的ID;
l???????? Msg:指定用來發(fā)送的消息;
l???????? wParam:同消息有關(guān)的字參數(shù);
l???????? lParam:同消息有關(guān)的長參數(shù);
l???????? 調(diào)用該函數(shù)時(shí),如果即將接收消息的線程沒有創(chuàng)建消息循環(huán),則該函數(shù)執(zhí)行失敗。
volatile修飾符的作用是告訴編譯器無需對該變量作任何的優(yōu)化,即無需將它放到一個(gè)寄存器中,并且該值可被外部改變。對于多線程引用的全局變量來說,volatile 是一個(gè)非常重要的修飾符。
?
終止線程
WaitForSingleObject函數(shù),其函數(shù)原型為:DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
?
hHandle為要監(jiān)視的對象(一般為同步對象,也可以是線程)的句柄;
dwMilliseconds為hHandle對象所設(shè)置的超時(shí)值,單位為毫秒;
當(dāng)在某一線程中調(diào)用該函數(shù)時(shí),線程暫時(shí)掛起,系統(tǒng)監(jiān)視hHandle所指向的對象的狀態(tài)。如果在掛起的dwMilliseconds毫秒內(nèi),線程所等待的對象變?yōu)橛行盘?hào)狀態(tài),則該函數(shù)立即返回;如果超時(shí)時(shí)間已經(jīng)到達(dá)dwMilliseconds毫秒,但hHandle所指向的對象還沒有變成有信號(hào)狀態(tài),函數(shù)照樣返回。參數(shù)dwMilliseconds有兩個(gè)具有特殊意義的值:0和INFINITE。若為0,則該函數(shù)立即返回;若為INFINITE,則線程一直被掛起,直到hHandle所指向的對象變?yōu)橛行盘?hào)狀態(tài)時(shí)為止。
本例程調(diào)用該函數(shù)的作用是按下IDC_START按鈕后,一直等到線程返回,再恢復(fù)IDC_START按鈕正常狀態(tài)。編譯運(yùn)行該例程并細(xì)心體會(huì)。
::TerminateThread(m_hThread, 0 );
m_hThread = NULL;
?
HANDLE m_hThread;//線程
DWORD m_hThreadId;? //線程ID
? static DWORD WINAPI ThreadProc( LPVOID lParam ) //線程函數(shù),非類成員
{
??? CString str;
??? str.Format(L"%d",AfxGetApp()->m_nThreadID);
//AfxGetApp()獲取當(dāng)前運(yùn)行的程序?qū)ο笾羔楥heheDlg*
??? ((CheheDlg*)lParam)->SetDlgItemText(IDC_EDIT1,str);//轉(zhuǎn)換對象指針
??? return 1;
}
//如果未傳遞當(dāng)前運(yùn)行的句柄。
??? CListBox *list;
??? list=(CListBox*)AfxGetApp()->GetMainWnd()->GetDlgItem(IDC_LIST1);
??? list->AddString(str);
AfxGetApp()獲取了該應(yīng)用程序的實(shí)例句柄
GetMainWnd()獲取了該應(yīng)用程序的主窗口
?
??? m_hThread=::CreateThread(NULL,0,ThreadProc,this,0,&m_hThreadId);//this當(dāng)前對象指針
?
線程的創(chuàng)建
使用CreateThread函數(shù)創(chuàng)建線程,CreateThread的原型如下:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags, // creation flags
LPDWORD lpThreadId
);
其中:
l???????? lpThreadAttributes表示創(chuàng)建線程的安全屬性,NT下有用。
l???????? dwStackSize指定線程棧的尺寸,如果為0則與進(jìn)程主線程棧相同。
l???????? lpStartAddress指定線程開始運(yùn)行的地址。
l???????? lpParameter表示傳遞給線程的32位的參數(shù)。
l???????? dwCreateFlages表示是否創(chuàng)建后掛起線程(取值CREATE_SUSPENDED),掛起后調(diào)用ResumeThread繼續(xù)執(zhí)行。
l???????? lpThreadId用來存放返回的線程ID。
?
??? h=CreateThread(NULL,NULL,ThreadProc,this,CREATE_SUSPENDED,NULL);//創(chuàng)建后掛起線程
ResumeThread(h);//恢復(fù)線程
SuspendThread(h);//掛起線程
?
static DWORD WINAPI ThreadProc( LPVOID lParam )
{
??? CString str;
??? CTime c;
??? while(1)
??? {
??????? c=CTime::GetCurrentTime();
??????? str=c.Format("%H:%M:%S");
??? ((CheheDlg*)lParam)->SetDlgItemText(IDC_EDIT1,str);
??? }
??????? return 0;
}
??
//獲取當(dāng)前線程的優(yōu)先級(jí)
??????? DWORD dw= GetThreadPriority(GetCurrentThread());
??????? str.Format(L"%x",dw);
?
//設(shè)置線程優(yōu)先級(jí):1:線程句柄? 2:優(yōu)先級(jí)THREAD_PRIORITY_NORMAL??? 0
BOOL WINAPI SetThreadPriority(
? __in????????? HANDLE hThread,
? __in????????? int nPriority
);
?
線程的終止
以下情況終止一個(gè)線程:
l???????? 調(diào)用了ExitThread函數(shù);
l???????? 線程函數(shù)返回:主線程返回導(dǎo)致ExitProcess被調(diào)用,其他線程返回導(dǎo)致ExitThread被調(diào)用;
l???????? 調(diào)用ExitProcess導(dǎo)致進(jìn)程的所有線程終止;
l???????? 調(diào)用TerminateThread終止一個(gè)線程;
l???????? 調(diào)用TerminateProcess終止一個(gè)進(jìn)程時(shí),導(dǎo)致其所有線程的終止。
l???????? 當(dāng)用TerminateProcess或者TerminateThread終止進(jìn)程或線程時(shí),DLL的入口函數(shù)DllMain不會(huì)被執(zhí)行(如果有DLL的話)。
?
線程同步
同步可以保證在一個(gè)時(shí)間內(nèi)只有一個(gè)線程對某個(gè)資源(如操作系統(tǒng)資源等共享資源)有控制權(quán)。共享資源包括全局變量、公共數(shù)據(jù)成員或者句柄等。同步還可以使得有關(guān)聯(lián)交互作用的代碼按一定的順序執(zhí)行。
同步對象有:Critical_section(關(guān)鍵段),Event(事件),Mutex(互斥對象),Semaphores(信號(hào)量)。
?
在Win32 API的基礎(chǔ)之上,MFC提供了處理線程的類和函數(shù)。處理線程的類是CWinThread,函數(shù)是AfxBeginThread、AfxEndThread等。
CWinThread是MFC線程類,它的成員變量m_hThread和m_hThreadID是對應(yīng)的Win32線程句柄和線程ID。
MFC明確區(qū)分兩種線程:用戶界面線程(User interface thread)和工作者線程(Worker thread)。用戶界面線程一般用于處理用戶輸入并對用戶產(chǎn)生的事件和消息作出應(yīng)答。工作者線程用于完成不要求用戶輸入的任務(wù),如耗時(shí)計(jì)算。
Win32 API并不區(qū)分線程類型,它只需要知道線程的開始地址以便它開始執(zhí)行線程。MFC為用戶界面線程特別地提供消息泵來處理用戶界面的事件。CWinApp對象是用戶界面線程對象的一個(gè)例子,CWinApp從類CWinThread派生并處理用戶產(chǎn)生的事件和消息。
?
為了防止用戶定義的消息ID與系統(tǒng)的消息ID沖突,MS(Microsoft)定義了一個(gè)宏WM_USER,小于WM_USER的ID被系統(tǒng)使用,大于WM_USER的ID被用戶使用。
?
PostMessage是Windows API(應(yīng)用程序接口) 中的一個(gè)常用函數(shù),用于將一條消息放入到消息隊(duì)列中。該函數(shù)將一個(gè)消息放入(寄送)到與指定窗口創(chuàng)建的線程相聯(lián)系消息隊(duì)列里,不等待線程處理消息就返回,是異步消息模式。消息隊(duì)列里的消息通過調(diào)用GetMessage和PeekMessage取得。函數(shù)原型:B00L PostMessage(HWNDhWnd,UINTMsg,WPARAMwParam,LPARAMlParam);
參數(shù)說明:
hWnd:其窗口程序接收消息的窗口的句柄??扇∮刑囟êx的兩個(gè)值:
HWND_BROADCAST:消息被寄送到系統(tǒng)的所有頂層窗口,包括無效或不可見的非自身擁有窗口、 被覆蓋的窗口和彈出式窗口。消息不被寄送到子窗口
NULL:此函數(shù)的操作和調(diào)用參數(shù)dwThread設(shè)置為當(dāng)前線程的標(biāo)識(shí)符PostThreadMessage函數(shù)一樣
Msg:指定被寄送的消息。
wParam:指定附加的消息特定的信息。
IParam:指定附加的消息特定的信息。
返回值:如果函數(shù)調(diào)用成功,返回非零值:如果函數(shù)調(diào)用失敗,返回值是零。若想獲得更多的錯(cuò)誤信息,請調(diào)用GetLastError函數(shù)。
?
使隸屬于同一進(jìn)程的各線程協(xié)調(diào)一致地工作稱為線程同步
?
使用 CCriticalSection 類
當(dāng)多個(gè)線程訪問一個(gè)獨(dú)占性共享資源時(shí),可以使用“臨界區(qū)”對象。任一時(shí)刻只有一個(gè)線程可以擁有臨界區(qū)對象,擁有臨界區(qū)的線程可以訪問被保護(hù)起來的資源或代碼段,其他希望進(jìn)入臨界區(qū)的線程將被掛起等待,直到擁有臨界區(qū)的線程放棄臨界區(qū)時(shí)為止,這樣就保證了不會(huì)在同一時(shí)刻出現(xiàn)多個(gè)線程訪問共享資源。
?
CCriticalSection類的用法非常簡單,步驟如下:
定義CCriticalSection類的一個(gè)全局對象(以使各個(gè)線程均能訪問),如CCriticalSection critical_section;
在訪問需要保護(hù)的資源或代碼之前,調(diào)用CCriticalSection類的成員Lock()獲得臨界區(qū)對象: critical_section.Lock();
?
在線程中調(diào)用該函數(shù)來使線程獲得它所請求的臨界區(qū)。如果此時(shí)沒有其它線程占有臨界區(qū)對象,則調(diào)用Lock()的線程獲得臨界區(qū);否則,線程將被掛起,并放入到一個(gè)系統(tǒng)隊(duì)列中等待,直到當(dāng)前擁有臨界區(qū)的線程釋放了臨界區(qū)時(shí)為止。
訪問臨界區(qū)完畢后,使用CCriticalSection的成員函數(shù)Unlock()來釋放臨界區(qū):critical_section.Unlock();
?
再通俗一點(diǎn)講,就是線程A執(zhí)行到critical_section.Lock();語句時(shí),如果其它線程(B)正在執(zhí)行critical_section.Lock();語句后且critical_section. Unlock();語句前的語句時(shí),線程A就會(huì)等待,直到線程B執(zhí)行完critical_section. Unlock();語句,線程A才會(huì)繼續(xù)執(zhí)行。
B、使用 CEvent 類
CEvent 類提供了對事件的支持。事件是一個(gè)允許一個(gè)線程在某種情況發(fā)生時(shí),喚醒另外一個(gè)線程的同步對象。例如在某些網(wǎng)絡(luò)應(yīng)用程序中,一個(gè)線程(記為A)負(fù)責(zé)監(jiān)聽通訊端口,另外一個(gè)線程(記為B)負(fù)責(zé)更新用戶數(shù)據(jù)。通過使用CEvent 類,線程A可以通知線程B何時(shí)更新用戶數(shù)據(jù)。每一個(gè)CEvent 對象可以有兩種狀態(tài):有信號(hào)狀態(tài)和無信號(hào)狀態(tài)。線程監(jiān)視位于其中的CEvent 類對象的狀態(tài),并在相應(yīng)的時(shí)候采取相應(yīng)的操作。
在MFC中,CEvent 類對象有兩種類型:人工事件和自動(dòng)事件。一個(gè)自動(dòng)CEvent 對象在被至少一個(gè)線程釋放后會(huì)自動(dòng)返回到無信號(hào)狀態(tài);而人工事件對象獲得信號(hào)后,釋放可利用線程,但直到調(diào)用成員函數(shù)ReSetEvent()才將其設(shè)置為無信號(hào)狀態(tài)。在創(chuàng)建CEvent 類的對象時(shí),默認(rèn)創(chuàng)建的是自動(dòng)事件。 CEvent 類的各成員函數(shù)的原型和參數(shù)說明如下:
?
1、CEvent(BOOL bInitiallyOwn=FALSE,
????????? BOOL bManualReset=FALSE,
????????? LPCTSTR lpszName=NULL,
????????? LPSECURITY_ATTRIBUTES lpsaAttribute=NULL);
bInitiallyOwn:指定事件對象初始化狀態(tài),TRUE為有信號(hào),F(xiàn)ALSE為無信號(hào);
bManualReset:指定要?jiǎng)?chuàng)建的事件是屬于人工事件還是自動(dòng)事件。TRUE為人工事件,F(xiàn)ALSE為自動(dòng)事件;
后兩個(gè)參數(shù)一般設(shè)為NULL,在此不作過多說明。
?
2、BOOL CEvent::SetEvent();
將 CEvent 類對象的狀態(tài)設(shè)置為有信號(hào)狀態(tài)。如果事件是人工事件,則 CEvent 類對象保持為有信號(hào)狀態(tài),直到調(diào)用成員函數(shù)ResetEvent()將 其重新設(shè)為無信號(hào)狀態(tài)時(shí)為止。如果CEvent 類對象為自動(dòng)事件,則在SetEvent()將事件設(shè)置為有信號(hào)狀態(tài)后,CEvent 類對象由系統(tǒng)自動(dòng)重置為無信號(hào)狀態(tài)。
如果該函數(shù)執(zhí)行成功,則返回非零值,否則返回零。 3、BOOL CEvent::ResetEvent();
該函數(shù)將事件的狀態(tài)設(shè)置為無信號(hào)狀態(tài),并保持該狀態(tài)直至SetEvent()被調(diào)用時(shí)為止。由于自動(dòng)事件是由系統(tǒng)自動(dòng)重置,故自動(dòng)事件不需要調(diào)用該函數(shù)。如果該函數(shù)執(zhí)行成功,返回非零值,否則返回零。我們一般通過調(diào)用WaitForSingleObject函數(shù)來監(jiān)視事件狀態(tài)。前面我們已經(jīng)介紹了該函數(shù)。由于語言描述的原因,CEvent 類的理解確實(shí)有些難度,但您只要通過仔細(xì)玩味下面例程,多看幾遍就可理解。
例程9 MultiThread9
?
CEvent eventWriteD;
線程WriteD執(zhí)行到 WaitForSingleObject(eventWriteD.m_hObject,INFINITE);處等待,直到事件eventWriteD為有信號(hào)該線程才往下執(zhí)行,因?yàn)閑ventWriteD對象是自動(dòng)事件,則當(dāng)WaitForSingleObject()返回時(shí),系統(tǒng)自動(dòng)把eventWriteD對象重置為無信號(hào)狀態(tài)
??? CWinThread *pWriteD=AfxBeginThread(WriteD,
??????? &m_ctrlD,
??????? THREAD_PRIORITY_NORMAL,
??????? 0,
??????? CREATE_SUSPENDED);
??? pWriteD->ResumeThread();
?
C、使用CMutex 類
互斥對象與臨界區(qū)對象很像.互斥對象與臨界區(qū)對象的不同在于:互斥對象可以在進(jìn)程間使用,而臨界區(qū)對象只能在同一進(jìn)程的各線程間使用。當(dāng)然,互斥對象也可以用于同一進(jìn)程的各個(gè)線程間,但是在這種情況下,使用臨界區(qū)會(huì)更節(jié)省系統(tǒng)資源,更有效率。
?
D、使用CSemaphore 類? afxmt.h
當(dāng)需要一個(gè)計(jì)數(shù)器來限制可以使用某個(gè)線程的數(shù)目時(shí),可以使用“信號(hào)量”對象。CSemaphore 類的對象保存了對當(dāng)前訪問某一指定資源的線程的計(jì)數(shù)值,該計(jì)數(shù)值是當(dāng)前還可以使用該資源的線程的數(shù)目。如果這個(gè)計(jì)數(shù)達(dá)到了零,則所有對這個(gè)CSemaphore 類對象所控制的資源的訪問嘗試都被放入到一個(gè)隊(duì)列中等待,直到超時(shí)或計(jì)數(shù)值不為零時(shí)為止。一個(gè)線程被釋放已訪問了被保護(hù)的資源時(shí),計(jì)數(shù)值減1;一個(gè)線程完成了對被控共享資源的訪問時(shí),計(jì)數(shù)值增1。這個(gè)被CSemaphore 類對象所控制的資源可以同時(shí)接受訪問的最大線程數(shù)在該對象的構(gòu)建函數(shù)中指定。
?
CSemaphore 類的構(gòu)造函數(shù)原型及參數(shù)說明如下:
?
CSemaphore (LONG lInitialCount=1,
??????????? LONG lMaxCount=1,
??????????? LPCTSTR pstrName=NULL,
??????????? LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);
lInitialCount:信號(hào)量對象的初始計(jì)數(shù)值,即可訪問線程數(shù)目的初始值;
lMaxCount:信號(hào)量對象計(jì)數(shù)值的最大值,該參數(shù)決定了同一時(shí)刻可訪問由信號(hào)量保護(hù)的資源的線程最大數(shù)目;
后兩個(gè)參數(shù)在同一進(jìn)程中使用一般為NULL,不作過多討論;
在用CSemaphore 類的構(gòu)造函數(shù)創(chuàng)建信號(hào)量對象時(shí)要同時(shí)指出允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。一般是將當(dāng)前可用資源計(jì)數(shù)設(shè)置為最大資源計(jì)數(shù),每增加一個(gè)線程對共享資源的訪問,當(dāng)前可用資源計(jì)數(shù)就會(huì)減1,只要當(dāng)前可用資源計(jì)數(shù)是大于0的,就可以發(fā)出信號(hào)量信號(hào)。但是當(dāng)前可用計(jì)數(shù)減小到0時(shí),則說明當(dāng)前占用資源的線程數(shù)已經(jīng)達(dá)到了所允許的最大數(shù)目,不能再允許其它線程的進(jìn)入,此時(shí)的信號(hào)量信號(hào)將無法發(fā)出。線程在處理完共享資源后,應(yīng)在離開的同時(shí)通過ReleaseSemaphore()函數(shù)將當(dāng)前可用資源數(shù)加1。
CSemaphore semaphoreWrite(2,2); //資源最多訪問線程2個(gè),當(dāng)前可訪問線程數(shù)2個(gè)
UINT WriteA(LPVOID pParam)
{
??? CEdit *pEdit=(CEdit*)pParam;
??? pEdit->SetWindowText("");
??? WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
??? CString str;
??? for(int i=0;i<10;i++)
??? {
??????? pEdit->GetWindowText(str);
??????? g_Array[i]=''A'';
??????? str=str+g_Array[i];
??? ??? pEdit->SetWindowText(str);
??????? Sleep(1000);
??? }
??? ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);
??? return 0;
}
在信號(hào)量對象有信號(hào)的狀態(tài)下,線程執(zhí)行到WaitForSingleObject語句處繼續(xù)執(zhí)行,同時(shí)可用線程數(shù)減1;若線程執(zhí)行到WaitForSingleObject語句時(shí)信號(hào)量對象無信號(hào),線程就在這里等待,直到信號(hào)量對象有信號(hào)線程才往下執(zhí)行。
?
#include
#include
#include
using namespacestd;
int nCount=50;???
DWORD ThreadID1;
DWORD ThreadID2;
DWORD ThreadID3;
HANDLE hMutex;
DWORD WINAPI TProc1(LPVOIDlParam)
{
??? while(nCount>0)
??? {
?????? Sleep(1);
?????? WaitForSingleObject(hMutex,INFINITE);
?????? printf("nCount=%d----線程n",nCount);
?????? nCount--;
?????? ReleaseMutex(hMutex);
??????
??? }
??? return 1;
}
DWORD WINAPI TProc2(LPVOIDlParam)
{
??? while(nCount>0)
??? {????? Sleep(1);
?????? WaitForSingleObject(hMutex,INFINITE);
?????? printf("nCount=%d----線程n",nCount);
?????? nCount--;
?????? ReleaseMutex(hMutex);
??? }
??? return 1;
}
?
?
int main(void)
{
??? hMutex=CreateMutex(NULL,NULL,NULL);
??? HANDLE h1=CreateThread(NULL,NULL,TProc1,NULL,NULL,&ThreadID1);
??? HANDLE h2=CreateThread(NULL,NULL,TProc2,NULL,NULL,&ThreadID2);
??? Sleep(5000);
??? return 0;
}