日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++:线程操作之CRITICAL_SECTION用法的介绍和例子理解

發(fā)布時間:2025/3/20 c/c++ 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++:线程操作之CRITICAL_SECTION用法的介绍和例子理解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

CRITICAL_SECTION

  • 介紹
  • 實例編輯1
    • 示例
  • 實例編輯2

介紹

CRITICAL_SECTION是每個線程中訪問臨界資源的那段代碼,不論是硬件臨界資源,還是軟件臨界資源,多個線程必須互斥地對它進(jìn)行訪問;

每個線程中訪問臨界資源的那段程序稱為臨界區(qū)(Critical Section)(臨界資源是一次僅允許一個線程使用的共享資源)。每次只準(zhǔn)許一個線程進(jìn)入臨界區(qū),進(jìn)入后不允許其他線程進(jìn)入。不論是硬件臨界資源,還是軟件臨界資源,多個線程必須互斥地對它進(jìn)行訪問。

線程進(jìn)入臨界區(qū)的調(diào)度原則是:
①如果有若干線程要求進(jìn)入空閑的臨界區(qū),一次僅允許一個線程進(jìn)入。
②任何時候,處于臨界區(qū)內(nèi)的線程不可多于一個。如已有線程進(jìn)入自己的臨界區(qū),則其它所有試圖進(jìn)入臨界區(qū)的線程必須等待。
③進(jìn)入臨界區(qū)的線程要在有限時間內(nèi)退出,以便其它線程能及時進(jìn)入自己的臨界區(qū)。
④如果線程不能進(jìn)入自己的臨界區(qū),則應(yīng)讓出CPU,避免線程出現(xiàn)“忙等”現(xiàn)象。

如果有多個線程試圖同時訪問臨界區(qū),那么在有一個線程進(jìn)入后其他所有試圖訪問此臨界區(qū)的線程將被掛起,并一直持續(xù)到進(jìn)入臨界區(qū)的線程離開。臨界區(qū)在被釋放后,其他線程可以繼續(xù)搶占,并以此達(dá)到用原子方式操作共享資源的目的。

臨界區(qū)在使用時以CRITICAL_SECTION結(jié)構(gòu)對象保護(hù)共享資源,并分別用**EnterCriticalSection()和LeaveCriticalSection()**函數(shù)去標(biāo)識和釋放一個臨界區(qū)。
所用到的CRITICAL_SECTION結(jié)構(gòu)對象必須經(jīng)過InitializeCriticalSection()的初始化后才能使用,而且必須確保所有線程中的任何試圖訪問此共享資源的代碼都處在此臨界區(qū)的保護(hù)之下。
否則臨界區(qū)將不會起到應(yīng)有的作用,共享資源依然有被破壞的可能。

簡單來說,EnterCriticalSection沒有給資源加鎖,只是給線程加了鎖,對于加了同一種鎖的線程,只能依次執(zhí)行,不許同步執(zhí)行,

其實,CRITICAL_SECTION是不能夠“鎖定”資源的,它能夠完成的功能,是同步不同線程的代碼段。簡單說,當(dāng)一個線程執(zhí)行了EnterCritialSection之后,臨界區(qū)結(jié)構(gòu)對象cs里面的信息便被修改,以指明哪一個線程占用了它。而此時,并沒有任何資源被“鎖定”。不管什么資源,其它線程都還是可以訪問的(當(dāng)然,執(zhí)行的結(jié)果可能是錯誤的)。只不過,在這個線程尚未執(zhí)行LeaveCriticalSection之前,其它線程碰到EnterCritialSection語句的話,就會處于等待狀態(tài),相當(dāng)于線程被掛起了。 這種情況下,就起到了保護(hù)共享資源的作用。

也正由于CRITICAL_SECTION是這樣發(fā)揮作用的,所以,必須把每一個線程中訪問共享資源的語句都放在EnterCritialSection和LeaveCriticalSection之間。這是初學(xué)者很容易忽略的地方。

什么時候可以用到:
線程不多時,全部為他們加同一種鎖,使他們依次執(zhí)行
或者加兩種鎖,兩個方式同時執(zhí)行

但是對于不加鎖或者加了不同鎖的線程,可以同步執(zhí)行:
如果用到兩個CRITICAL_SECTION,比如說:

第一個線程已經(jīng)執(zhí)行了EnterCriticalSection(&cs)并且還沒有執(zhí)行LeaveCriticalSection(&cs),這時另一個線程想要執(zhí)行EnterCriticalSection(&cs2),這種情況是可以的(除非cs2已經(jīng)被第三個線程搶先占用了)。這也就是多個CRITICAL_SECTION實現(xiàn)同步的思想。

實例編輯1

比如說我們定義了一個共享資源dwTime[100],兩個線程ThreadFuncA和ThreadFuncB都對它進(jìn)行讀寫操作。當(dāng)我們想要保證 dwTime[100]的操作完整性,即不希望寫到一半的數(shù)據(jù)被另一個線程讀取,那么用CRITICAL_SECTION來進(jìn)行線程同步如下:

第一個線程函數(shù):

DWORD WINAPI ThreadFuncA(LPVOID lp) {EnterCriticalSection(&cs);...// 操作dwTime...LeaveCriticalSection(&cs);return 0; }

dwTime并沒有和任何東西對應(yīng),它仍然是任何其它線程都可以訪問的。不要錯誤地以為,此時cs對dwTime進(jìn)行了鎖定操作,dwTime處于cs的保護(hù)之中。一個“自然而然”的想法就是——cs和dwTime一一對應(yīng)上了。這么想,就大錯特錯了。

如果你像如下的方式來寫第二個線程,那么就會有問題:

DWORD WINAPI ThreadFuncB(LPVOID lp) {...// 操作dwTime...return 0; }

當(dāng)線程ThreadFuncA執(zhí)行了EnterCriticalSection(&cs),并開始操作dwTime[100]的時候,線程ThreadFuncB可能隨時醒過來,也開始操作dwTime[100],這樣,dwTime[100]中的數(shù)據(jù)就被破壞了。

為了讓 CRITICAL_SECTION發(fā)揮作用,我們必須在訪問dwTime的任何一個地方都加上 EnterCriticalSection(&cs)和LeaveCriticalSection(&cs)語句。所以,必須按照下面的方式來寫第二個線程函數(shù):

DWORD WINAPI ThreadFuncB(LPVOID lp) {EnterCriticalSection(&cs);...// 操作dwTime...LeaveCriticalSection(&cs);return 0; }

這樣,當(dāng)線程ThreadFuncB醒過來時,它遇到的第一個語句是EnterCriticalSection(&cs),這個語句將對cs變量進(jìn)行訪問。如果這個時候第一個線程仍然在操作dwTime[100],cs變量中包含的值將告訴第二個線程,已有其它線程占用了cs。因此,第二個線程的 EnterCriticalSection(&cs)語句將不會返回,而處于掛起等待狀態(tài)。直到第一個線程執(zhí)行了 LeaveCriticalSection(&cs),第二個線程的EnterCriticalSection(&cs)語句才會返回,并且繼續(xù)執(zhí)行下面的操作。

說明: 這個過程實際上是通過限制有且只有一個函數(shù)進(jìn)入CriticalSection變量來實現(xiàn)代碼段同步的。簡單地說,對于同一個CRITICAL_SECTION,當(dāng)一個線程執(zhí)行了EnterCriticalSection而沒有執(zhí)行 LeaveCriticalSection的時候,其它任何一個線程都無法完全執(zhí)行EnterCriticalSection而不得不處于等待狀態(tài)。

再次強調(diào)一次,沒有任何資源被“鎖定”,CRITICAL_SECTION這個東東不是針對于資源的,而是針對于不同線程間的代碼段的!我們能夠用它來進(jìn)行所謂資源的“鎖定”,其實是因為我們在任何訪問共享資源的地方都加入了EnterCriticalSection和 LeaveCriticalSection語句,使得同一時間只能夠有一個線程的代碼段訪問到該共享資源而已(其它想訪問該資源的代如果是兩個CRITICAL_SECTION,就以此類推。碼段不得不等待)。

如果是兩個CRITICAL_SECTION,就以此類推。

示例

再舉個極端的例子,可以幫助你理解CRITICAL_SECTION這個東東:
第一個線程函數(shù):

DWORD WINAPI ThreadFuncA(LPVOID lp) {EnterCriticalSection(&cs);for(int i=0;i <1000;i++)Sleep(1000);LeaveCriticalSection(&cs);return 0; }

第二個線程函數(shù):

DWORD WINAPI ThreadFuncB(LPVOID lp) {EnterCriticalSection(&cs);index=2;LeaveCriticalSection(&cs);return 0; }

這種情況下,第一個線程中間總共Sleep了1000秒鐘!它顯然沒有對任何資源進(jìn)行什么“有意識”的保護(hù);而第二個線程是要訪問資源index的,但是由于第一個線程占用了cs,一直沒有Leave,而導(dǎo)致第二個線程不得不等上1000秒鐘……

你會看到第二個線程在1000秒鐘之后開始執(zhí)行index=2這個語句。也就是說,CRITICAL_SECTION其實并不理會你關(guān)心的具體共享資源,它只關(guān)系你是否占用了cs

實例編輯2

下面通過一段代碼展示了臨界區(qū)在保護(hù)多線程訪問的共享資源中的作用。
通過兩個線程來分別對全局變量g_cArray[10]進(jìn)行寫入操作,用臨界區(qū)結(jié)構(gòu)對象g_cs來保持線程的同步,并在開啟線程前對其進(jìn)行初始化。

為了使實驗效果更加明顯,體現(xiàn)出臨界區(qū)的作用,在線程函數(shù)對共享資源g_cArray[10]的寫入時,以Sleep()函數(shù)延遲1毫秒,使其他線程同其搶占CPU的可能性增大。如果不使用臨界區(qū)對其進(jìn)行保護(hù),則共享資源數(shù)據(jù)將被破壞(參見圖1(a)所示計算結(jié)果),而使用臨界區(qū)對線程保持同步后則可以得到正確的結(jié)果(參見圖1(b)所示計算結(jié)果)。代碼實現(xiàn)清單附下:

// 臨界區(qū)結(jié)構(gòu)對象 CRITICAL_SECTION g_cs; // 共享資源 char g_cArray[10]; UINT ThreadProc10(LPVOID pParam) { // 進(jìn)入臨界區(qū) EnterCriticalSection(&g_cs); // 對共享資源進(jìn)行寫入操作 for (int i = 0; i < 10; i++) { g_cArray = a; Sleep(1); } // 離開臨界區(qū) LeaveCriticalSection(&g_cs); return 0; }UINT ThreadProc11(LPVOID pParam) { // 進(jìn)入臨界區(qū) EnterCriticalSection(&g_cs); // 對共享資源進(jìn)行寫入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = b; Sleep(1); } // 離開臨界區(qū) LeaveCriticalSection(&g_cs); return 0; } …… void CSample08View::OnCriticalSection() { // 初始化臨界區(qū) InitializeCriticalSection(&g_cs); // 啟動線程 AfxBeginThread(ThreadProc10, NULL); AfxBeginThread(ThreadProc11, NULL); // 等待計算完畢 Sleep(300); // 報告計算結(jié)果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); } 下面看代碼(全部加同一種鎖): UINT CThreadLockTestOneDlg::fThread1(LPVOID lpParameter) { CThreadLockTestOneDlg *pthis = (CThreadLockTestOneDlg*)lpParameter; int i, j; EnterCriticalSection(&pthis->g_cs); for (i = 1; i<=10; i++) { pthis->value++; pthis->str.Format(pthis->str + "threadOne: %d\r\n", pthis->value); pthis->SetDlgItemTextA(IDC_EDIT1, pthis->str); } LeaveCriticalSection(&pthis->g_cs); return 0; } UINT CThreadLockTestOneDlg::fThread2(LPVOID lpParameter) { CThreadLockTestOneDlg *pthis = (CThreadLockTestOneDlg*)lpParameter; int i, j; EnterCriticalSection(&pthis->g_cs); for (i = 1; i<=10; i++) { pthis->value++; pthis->str.Format(pthis->str + "\nthreadSecond: %d\r\n", pthis->value); pthis->SetDlgItemTextA(IDC_EDIT1, pthis->str); } LeaveCriticalSection(&pthis->g_cs); return 0; } 此時開啟兩個線程的話 InitializeCriticalSection(&g_cs); str = ""; value = 0; hThread1 = AfxBeginThread((AFX_THREADPROC)fThread1, this); hThread2 = AfxBeginThread((AFX_THREADPROC)fThread2, this);

執(zhí)行結(jié)果為:(優(yōu)先執(zhí)行先加鎖的)
threadOne: 1
threadOne: 2
threadOne: 3
threadOne: 4
threadOne: 5
threadOne: 6
threadOne: 7
threadOne: 8
threadOne: 9
threadOne: 10
threadSecond: 11
threadSecond: 12
threadSecond: 13
threadSecond: 14
threadSecond: 15
threadSecond: 16
threadSecond: 17
threadSecond: 18
threadSecond: 19
threadSecond: 20
而第二段代碼(一個加鎖,一個不加鎖):
UINT CThreadLockTestOneDlg::fThread1(LPVOID lpParameter)
{
CThreadLockTestOneDlg pthis = (CThreadLockTestOneDlg)lpParameter;
int i, j;
EnterCriticalSection(&pthis->g_cs);
for (i = 1; i<=10; i++)
{
pthis->value++;
pthis->str.Format(pthis->str + “threadOne: %d\r\n”, pthis->value);
pthis->SetDlgItemTextA(IDC_EDIT1, pthis->str);
}
LeaveCriticalSection(&pthis->g_cs);
return 0;
}
UINT CThreadLockTestOneDlg::fThread2(LPVOID lpParameter)
{
CThreadLockTestOneDlg pthis = (CThreadLockTestOneDlg)lpParameter;
int i, j;
for (i = 1; i<=10; i++)
{
pthis->value++;
pthis->str.Format(pthis->str + “\nthreadSecond: %d\r\n”, pthis->value);
pthis->SetDlgItemTextA(IDC_EDIT1, pthis->str);
}
return 0;
}
執(zhí)行結(jié)果為(都可以訪問資源,同步執(zhí)行):
threadOne: 1
threadSecond: 2
threadOne: 3
threadSecond: 4
threadOne: 5
threadSecond: 6
threadOne: 7
threadSecond: 8
threadOne: 9
threadSecond: 10
threadOne: 11
threadSecond: 12
threadOne: 13
threadSecond: 14
threadOne: 15
threadSecond: 16
threadOne: 17
threadSecond: 18
threadOne: 19
threadSecond: 20
第三段代碼(加了不同的鎖):
UINT CThreadLockTestOneDlg::fThread1(LPVOID lpParameter)
{
CThreadLockTestOneDlg pthis = (CThreadLockTestOneDlg)lpParameter;
int i, j;
EnterCriticalSection(&pthis->g_cs);
for (i = 1; i<=10; i++)
{
pthis->value++;
pthis->str.Format(pthis->str + “threadOne: %d\r\n”, pthis->value);
pthis->SetDlgItemTextA(IDC_EDIT1, pthis->str);
}
LeaveCriticalSection(&pthis->g_cs);
return 0;
}
UINT CThreadLockTestOneDlg::fThread2(LPVOID lpParameter)
{
CThreadLockTestOneDlg pthis = (CThreadLockTestOneDlg)lpParameter;
int i, j;
EnterCriticalSection(&pthis->g_cs2);
for (i = 1; i<=10; i++)
{
pthis->value++;
pthis->str.Format(pthis->str + “\nthreadSecond: %d\r\n”, pthis->value);
pthis->SetDlgItemTextA(IDC_EDIT1, pthis->str);
}
LeaveCriticalSection(&pthis->g_cs2);
return 0;
}
執(zhí)行結(jié)果(同步執(zhí)行):
threadOne: 1
threadSecond: 2
threadOne: 3
threadSecond: 4
threadOne: 5
threadSecond: 6
threadOne: 7
threadSecond: 8
threadOne: 9
threadSecond: 10
threadOne: 11
threadSecond: 12
threadOne: 13
threadSecond: 14
threadOne: 15
threadSecond: 16
threadOne: 17
threadSecond: 18
threadOne: 19
threadSecond: 20

在使用臨界區(qū)時,一般不允許其運行時間過長,只要進(jìn)入臨界區(qū)的線程還沒有離開,其他所有試圖進(jìn)入此臨界區(qū)的線程都會被掛起而進(jìn)入到等待狀態(tài),并會在一定程度上影響程序的運行性能。尤其需要注意的是不要將等待用戶輸入或是其他一些外界干預(yù)的操作包含到臨界區(qū)。如果進(jìn)入了臨界區(qū)卻一直沒有釋放,同樣也會引起其他線程的長時間等待。換句話說,在執(zhí)行了EnterCriticalSection()語句進(jìn)入臨界區(qū)后無論發(fā)生什么,必須確保與之匹配的LeaveCriticalSection()都能夠被執(zhí)行到??梢酝ㄟ^添加結(jié)構(gòu)化異常處理代碼來確保LeaveCriticalSection()語句的執(zhí)行。雖然臨界區(qū)同步速度很快,但卻只能用來同步本進(jìn)程內(nèi)的線程,而不可用來同步多個進(jìn)程中的線程。
CRITICAL_SECTION 所使用的頭文件<windows.h>

總結(jié)

以上是生活随笔為你收集整理的C++:线程操作之CRITICAL_SECTION用法的介绍和例子理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。