windows編程學習——關鍵代碼段

 多線程學習篇(一)關鍵代碼段

一、基本概念

關鍵代碼段:關鍵代碼段也稱之爲臨界區,工作在用戶模式下。它是一小段代碼,在執行前需要獨佔對一些共享資源的訪問權。

 

優點:關鍵代碼段工作在用戶模式下,因此它簡單,執行速度快。

 

缺點:不能在多個進程之間對線程進行同步(原因:它不是內核對象)

二、API

1、關鍵段相關API

CRITICAL_SECTION結構體:這是一個未公開內部信息的結構體,通過這個結構,我們就可以使用API對想要獨佔的代碼進行操作。

使用該結構的條件:1、所有想要訪問資源的線程,必須知道用來保護資源的CRITICAL_SECTION結構的地址。我們通過傳入該結構體的地址,進行操作。2、任何線程在訪問被保護的資源之前,都必須對這個結構體進行初始化。

VOID InitializeCriticalSection(PCRITICAL_SECTION pcs);

功能:初始化臨界區

參數:psc指向CRITICAL_SECTION結構體的地址

 

VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);

功能:重置結構體中的成員變量(而不是刪除它)。注意:如果刪除的話,別的線程還在使用一個關鍵段,就會產生不可預料的結果。

參數:psc指向CRITICAL_SECTION結構體的地址

 

VOID EnterCriticalSection(PCRITICAL_SECTION pcs)

功能:EnterCriticalSection會檢查結構中的成員變量,這些變量表示是否有線程正在進行訪問。以及哪個線程正在訪問。如果沒有線程訪問,則獲取訪問權限。如果有線程正在訪問,則等待。

注意:這個API內部的實現,其實只是對這個結構體做了一些列的檢測,它的價值就在於它可以原子性的執行所有的檢測。

參數:psc指向CRITICAL_SECTION結構體的地址

 

BOOL TryEnterCriticalSection(PCRITICAL_SECTION pcs)

功能:和EnterCriticalSection作用相同,只是這個API不會因爲另一個線程正在使用pcs這個結構而進入等待狀態,它會根據返回值判斷是否可以訪問。然後繼續執行下面的代碼

參數:psc指向CRITICAL_SECTION結構體的地址

 

VOID LeaveCriticalSection(PCRITICAL_SECTION pcs)

功能:更新成員函數,以表示沒有任何線程訪問被保護資源,同時檢查其他線程有沒有因爲調用EnterCriticalSection而處於等待狀態。如果有,則更新成員函數,將一個等待的線程切回調度狀態

參數:psc指向CRITICAL_SECTION結構體的地址

 

2、關鍵段加自旋鎖相關API

由於當線程試圖進入一個關鍵段,但這個關鍵段正在被另一個線程佔用的時候,函數會立即把調用的線程切換到等待狀態。因爲線程狀態切換需要在內核中進行,因此開銷非常大(大約1000個CPU週期)。這樣,在一些需要及時獲取資源的線程會有很大的影響。爲此,windows爲大家在關鍵段中加入了自旋鎖,保證了實時性,但卻浪費了大量CPU資源。

注意:關鍵段加自旋鎖在單CPU上使用毫無用處,因爲如果一個線程正在循環,那麼佔用資源的線程就沒有時間放棄對資源的訪問權。

BOOL InitializeCriticalSectionAndSpinCount(

PCRITICAL_SECTION pcs,

DOWRD dwSpinCount

)

功能:初始化一個PCRITICAL_SECTION結構體,和自旋鎖自旋轉次數。用這個API初始化後,當調用EnterCriticalSection()的時候,會用一個鎖不斷循環,試圖獲得對資源的訪問權。只有嘗試失敗時,纔會切換到等待狀態。

參數:psc指向CRITICAL_SECTION結構體的地址

  dwSpinCount我們希望旋轉的次數。

   DWORD SetCriticalSectionSpinCount(

PCRITICAL_SECTION pcs,

DOWRD dwSpinCount

)

    功能:改變設置的自旋鎖自旋轉的次數

 

 

 

三、練習 

問題:現在使用關鍵代碼段技術,完成一個火車站的售票系統,要求建立兩個線程作爲售票口,分別向外出售火車票。票數總數爲100。編寫程序,使得它們可以正常的出售。

 

#include <Windows.h>
#include <stdio.h>


DWORD WINAPI Sell_Window1(LPVOID lpParameter);
DWORD WINAPI Sell_Window2(LPVOID lpParameter);


int tickets = 100;
CRITICAL_SECTION cs;


void main()
{
HANDLE hThread1;
HANDLE hThread2;
hThread1 = CreateThread(NULL,0,Sell_Window1,NULL,0,NULL);
hThread2 = CreateThread(NULL,0,Sell_Window2,NULL,0,NULL);
InitializeCriticalSection(&cs);
Sleep(4000);
CloseHandle(hThread1);
CloseHandle(hThread2);
DeleteCriticalSection(&cs);
}


DWORD WINAPI Sell_Window1(LPVOID lpParameter)
{
while (1)
{
EnterCriticalSection(&cs);
if (tickets > 0)
{
printf("窗口1出售第%d張票\n",tickets--);
LeaveCriticalSection(&cs);
}
else
{
LeaveCriticalSection(&cs);
break;
}
}
return 0;
}
DWORD WINAPI Sell_Window2(LPVOID lpParameter)
{
while (1)
{
EnterCriticalSection(&cs);
if (tickets > 0)
{
printf("窗口2出售第%d張票\n",tickets--);
LeaveCriticalSection(&cs);
}
else
{
LeaveCriticalSection(&cs);
break;
}
}
return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章