国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
《Windows核心編程》之五 --線程的同步 - Shipfi (春暖花開,開滿我的陽臺) - 博客園

《Windows核心編程》之五 --線程的同步

用戶方式中線程的同步

 

1.線程間的互鎖
      
有時(shí)線程的某些步驟需要以原子的方式來進(jìn)行操作,尤其涉及到線程對內(nèi)存,資源的訪問的時(shí)候。Windows中,解決這個(gè)問題的辦法之一就是對線程中某些操作進(jìn)行互鎖。那么這些操作就會不被中斷而進(jìn)行。

       比如,使線程互鎖以對某個(gè)變量值進(jìn)行遞增操作的函數(shù):

          LONG InterlockedExchangeAdd(PLONG plAddend,  LONG Increment);

      

       舉一例:

       long g_x=0;

       DWORD WINAPI ThreadFun1(PVOID pvParam)

{

InterlockedExchangeAdd(&g_x,1);
}

       DWORD WINAPI ThreadFun2(PVOID pvParam)

{

InterlockedExchangeAdd(&g_x,2);
}

 

通過這個(gè)小小的修改,g_x就可以以原子的方式來進(jìn)行遞增。

不必清楚地了解互鎖函數(shù)是如何工作的。重要的是要知道,無論編譯器怎樣生成代碼,無論計(jì)算機(jī)中安裝了多少個(gè)C P U,它們都能保證以原子操作方式來修改一個(gè)值。還必須保證傳遞給這些函數(shù)的變量地址正確地對齊,否則這些函數(shù)就會運(yùn)行失敗

 

還有幾個(gè)修改變量的互鎖函數(shù)如下所示:

//改變某個(gè)變量值
LONG InterlockedExchange(PLONG plTarget, LONG lValue);
//改變指針的值
PVOID InterlockedExchangePointer(PVOID* ppvTarget, PVOID pvValue);

 

 

實(shí)現(xiàn)循環(huán)鎖

可以用InterlockedExChange來實(shí)現(xiàn)循環(huán)鎖的功能,所謂循環(huán)鎖,就是在線程1中如果要對變量進(jìn)行操作,要先查看這個(gè)變量(或資源)有沒有被其它線程用到,如果是,則一直循環(huán),則到其它線程放棄對該變量(或資源)的控制。如果否,直接可以對該變量(或資源)進(jìn)行操作。

如:

 

BOOL g_fResourceInUse = FALSE;

void Func1()
{

       //Wait to access the resource.  等待資源

       while(InterlockedExchange(&g_fResourceInUse, TRUE) == TRUE)

          Sleep(0);

     //Access the resource.  //獲取資源

      ...//

      

     //We no longer need to access the resource.

      InterlockedExchange(&g_fResourceInUse, FALSE);  //釋放資源

}

 

其它線程如要使用資源也如上代碼所示。

 

OKwh i l e循環(huán)是循環(huán)運(yùn)行的(假使本線程是ThreadA),它將g _ f R e s o u r c e I n U s e中的值改為TR U E,并查看它的前一個(gè)值,以了解它是否是T R UE。如果是,則表示已經(jīng)有線程(假使為ThreadB)使用了,它就要等待,直到ThreadB線程執(zhí)行InterLockedExchange(&g_fResourceInUse, FALSE); 操作,則ThreadA它就可以退出while循環(huán),然后獲取資源,并且,它對g_fResourceInUse設(shè)置為TURE,其它線程(假使ThreadC)如要使用,則將如剛才ThreadA般等待。直到ThreadAInterlockedExchange(&g_fResourceInUse, FALSE);執(zhí)行完為止。

 

 

 

 

 

2.關(guān)鍵代碼段

 

       關(guān)鍵代碼段是指這樣一段代碼,它可以在代碼執(zhí)行前,獨(dú)占對某資源的訪問權(quán)。這是能讓若干代碼能夠“以原子方式”來使用資源的另一種方法。

       注意,關(guān)鍵代碼段運(yùn)行中仍是可以被系統(tǒng)撤銷它的時(shí)間片,再調(diào)度給別的線程。但是,可調(diào)度線程有了變化,訪問該線程需要使用的資源的其它任何線程將得不到調(diào)度。

      

       關(guān)鍵代碼段的使用:

const int MAX_TIMES = 1000;
int   g_nIndex = 0;
DWORD g_dwTimes[MAX_TIMES];
CRITICAL_SECTION g_cs;
 
DWORD WINAPI FirstThread(PVOID pvParam) 
{
   while(g_nIndex < MAX_TIMES) 
   {
      EnterCriticalSection(&g_cs);
      g_dwTimes[g_nIndex] = GetTickCount();
      g_nIndex++;
      LeaveCriticalSection(&g_cs);
   }
   return(0);
}
DWORD WINAPI SecondThread(PVOID pvParam) 
{
   while(g_nIndex < MAX_TIMES)
   {
      EnterCriticalSection(&g_cs);
      g_nIndex++;
      g_dwTimes[g_nIndex - 1] = GetTickCount();
      LeaveCriticalSection(&g_cs);
   }
   return(0);
} 

      

       注:關(guān)鍵代碼段有個(gè)CRITICAL_SECTION結(jié)構(gòu)控制。然后用EnterCriticalSectionLeaveCriticalSection封裝了要接觸共享資源的代碼。當(dāng)無法用互鎖函數(shù)來解決同步問題時(shí),你應(yīng)該試用關(guān)鍵代碼段。關(guān)鍵代碼段的優(yōu)點(diǎn)在于它們的使用非常容易,它們在內(nèi)部使用互鎖函數(shù),這樣它們就能夠迅速運(yùn)行。

 

 

 

 

3.關(guān)于使用互鎖與關(guān)鍵代碼段的弊端與優(yōu)點(diǎn):

 

       用互鎖與關(guān)鍵代碼段來實(shí)現(xiàn)線程的同步,稱為用戶方式的線程同步。它的優(yōu)點(diǎn)是速度非???,如果在程序中要強(qiáng)調(diào)效率問題,則使用以上兩種方法來同步線程是比較好的。

       雖然用戶方式的線程同步機(jī)制有速度快的優(yōu)點(diǎn),但是,很多時(shí)候是不適用的。例如,互鎖函數(shù)只能在單值上運(yùn)行,而且它無法是線程進(jìn)入等待狀態(tài)。雖然可以用關(guān)鍵代碼段讓線程進(jìn)入等待狀態(tài),但是關(guān)鍵代碼段只能對在同一個(gè)進(jìn)程中的線程實(shí)施同步,而且,用關(guān)鍵代碼段,很容易引起死鎖,因?yàn)樗拇a無法設(shè)置超時(shí)值。

       還有一種同步,就是用內(nèi)核對象來實(shí)施同步。

      

 

4.內(nèi)核對象實(shí)施同步:

       內(nèi)核對象的適應(yīng)性要遠(yuǎn)遠(yuǎn)勝于用戶方式,唯一不足即是速度慢。

       內(nèi)核對象中,有一個(gè)標(biāo)志是通知狀態(tài)標(biāo)志,比如進(jìn)程,它運(yùn)行的時(shí)候,通知狀態(tài)標(biāo)志是末通知的,等到進(jìn)程結(jié)束,即變成已通知狀態(tài)。通知狀態(tài)標(biāo)志是個(gè)布爾值。不光進(jìn)程,很多內(nèi)核對象都有這個(gè)標(biāo)志,比如線程,作業(yè),文件,控制臺輸入,信標(biāo),互斥對象,事件,文件修改器等。

       同核對象的同步就是要介紹線程等待某個(gè)內(nèi)核對象變?yōu)橐淹ㄖ獱顟B(tài)的函數(shù)。然后講述Windows提供的專門用來幫助實(shí)現(xiàn)線程同步的各種內(nèi)核對象,如:事件,等待計(jì)數(shù)器,信標(biāo)和互斥對象。

       當(dāng)線程等待的某個(gè)內(nèi)核對象處于末通知狀態(tài),則線程不可調(diào)度,如果該內(nèi)核對象處于已通知狀態(tài),則變?yōu)榭烧{(diào)度線程。

       OK,下面講內(nèi)核對象同步要使用的函數(shù)與方法

 

 

5.等待函數(shù) WaitForSingleObjectWaitForMultipleObject

       DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);
       其中,hObject就是一個(gè)能夠支持被通知/末通知的內(nèi)核對象。比如:

       WaitForSingleObject(hProcess, INFINITE);

       就是告訴系統(tǒng),等待到hProcess進(jìn)程結(jié)束為止。第二個(gè)參數(shù)dwMilliseconds告訴系統(tǒng),將永遠(yuǎn)等下去,直到對象變成通知狀態(tài)。

       書上說:傳遞I N F I N I T E有些危險(xiǎn)。如果對象永遠(yuǎn)不變?yōu)橐淹ㄖ獱顟B(tài),那么調(diào)用線程永遠(yuǎn)不會被喚醒,它將永遠(yuǎn)處于死鎖狀態(tài),不過,它不會浪費(fèi)寶貴的C P U時(shí)間。

       可以為第二個(gè)參數(shù)傳遞一個(gè)值,表示等待多長時(shí)間。如果對象還沒變成已通知狀態(tài),那么就換起線程。不要為第二個(gè)參數(shù)傳遞0,因?yàn)檫@樣線程總是立即被喚醒。

      

       線程可以同時(shí)查看多個(gè)內(nèi)核對象的通知狀態(tài),使用函數(shù)WaitForMultipleObjects函數(shù):

    DWORD WaitForMultipleObjects(DWORD dwCount,
   CONST HANDLE* phObjects, 
   BOOL fWaitAll, DWORD dwMilliseconds);

       dwCount是想讓線程查看內(nèi)核對象的數(shù)量,phObjects是指向內(nèi)核對象句柄數(shù)組的指針。

       可以以兩種不同的方式來使用WaitForMultipleObjects函數(shù)。一種方式是讓線程進(jìn)入等待狀態(tài),直到指定內(nèi)核對象中的任何一個(gè)變?yōu)橐淹ㄖ獱顟B(tài)。另一種方式是讓線程進(jìn)入等待狀態(tài),直到所有指定的內(nèi)核對象都變?yōu)橐淹ㄖ獱顟B(tài)。fWaitAll參數(shù)告訴該函數(shù),你想要讓它使用何種方式。如果為該參數(shù)傳遞TRUE,那么在所有對象變?yōu)橐淹ㄖ獱顟B(tài)之前,該函數(shù)將不允許調(diào)用線程運(yùn)行。

    dwMilliseconds是設(shè)定時(shí)間,與上面的函數(shù)作用相同。

 

    調(diào)用返回值:

    對于有些內(nèi)核對象來說,成功地調(diào)用Wa i t F o r S i n g l e O b j e c tWa i t F o r M u l t i p l e O b j e c t s,實(shí)際上會改變對象的狀態(tài)。成功地調(diào)用是指函數(shù)發(fā)現(xiàn)對象已經(jīng)得到通知并且返回一個(gè)相對于WA I T _ O B J E C T _ 0的值。如果函數(shù)返回WA I T _ T I M E O U TWA I T _ FA I L E D,那么調(diào)用就沒有成功。如果函數(shù)調(diào)用沒有成功,對象的狀態(tài)就不可能改變。

 

6.事件內(nèi)核對象

    事件對象是最基本的對象,它包含引用計(jì)數(shù),一個(gè)用于指定該事件是自動重置還是人工重置事件的布爾值,另一個(gè)用于指明該事件是處于未通知還是已通知狀態(tài)。

    事件能夠通知一個(gè)操作已完成。有兩種事件,一種是人工重置事件,一種是自動重置事件。有什么區(qū)別呢?書上講到:

    當(dāng)人工重置的事件得到通知時(shí),等待該事件的所有線程均變?yōu)榭烧{(diào)度線程。當(dāng)一個(gè)自動重置的事件得到通知時(shí),等待該事件的線程中只有一個(gè)線程變?yōu)榭烧{(diào)度線程。

       什么時(shí)候該用事件對象?

       答:當(dāng)一個(gè)線程執(zhí)行初始化操作,然后通知另一個(gè)線程執(zhí)行剩余的操作時(shí),事件使用得最多。

 

       事件對象的創(chuàng)建:

    HANDLE CreateEvent(
   PSECURITY_ATTRIBUTES psa,
   BOOL fManualReset,
   BOOL fInitialState,PCTSTR pszName);

    參數(shù)中fManualReset是告訴系統(tǒng)是人工重置事件還是自動重置事件,fInitialState是表示初始化是已通知狀態(tài)還是未通知狀態(tài)。

       創(chuàng)建完成后返回事件對象的句柄,當(dāng)然,也可以用OpenEvent(DWORD fdwAccess,BOOL fInherit,PCTSTR pszName); 來獲得該對象。

      

       改變事件的通知狀態(tài)與末通知狀態(tài):

       SetEvent(HANDLE hEvent)設(shè)置事件為已通知狀態(tài)。

       ResetEvent(HANDLE hEvent)設(shè)置事件為未通知狀態(tài)。

       改變狀態(tài)就是這么容易。

 

 

       例子:

       書上舉了使用事件內(nèi)核對象一例,特意摘入下來:

      

       //定義事件Handle
HANDLE g_hEvent;

//主程序
int WINAPI WinMain()
{
    
//創(chuàng)建一個(gè)未通知狀態(tài)的事件
    g_hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
   
    
//啟動線程,但是線程并不會運(yùn)行,只會掛起,因?yàn)槊總€(gè)線程中都有WaitForSingleObject(g_hEvent..)的代碼,而現(xiàn)在g_hEvent是未通知狀態(tài)的。
   HANDLE hThread[3];
   DWORD dwThreadID;
   hThread[0] = _beginthreadex(NULL, 0, WordCount, NULL, 0, &dwThreadID);
   hThread[1] = _beginthreadex(NULL, 0, SpellCheck, NULL, 0, &dwThreadID);
   hThread[2] = _beginthreadex(NULL, 0, GrammarCheck, NULL, 0, &dwThreadID);

   
//打開文件并寫入內(nèi)存
   OpenFileAndReadContentsIntoMemory( );

   
//以上操作執(zhí)行完后,可以讓線程啟動了。設(shè)置線程為已通知狀態(tài)
    SetEvent(g_hEvent); //啟動三個(gè)線程
}


DWORD WINAPI SpellCheck(PVOID pvParam)
{
   
//Wait until the file's data is in memory.
   WaitForSingleObject(g_hEvent, INFINITE);
   
//Access the memory block.  .....
   return(0);
}

DWORD WINAPI GrammarCheck(PVOID pvParam)
{
   
//Wait until the file's data is in memory.
   WaitForSingleObject(g_hEvent, INFINITE);
   
//Access the memory block.  ......
   
return(0);
}

 

DWORD WINAPI WordCount(PVOID pvParam)
{
  
//Wait until the file's data is in memory.
   WaitForSingleObject(g_hEvent, INFINITE);
  
//Access the memory block.  .....
   return(0);
}

 

書上對這段代碼的解釋:

         當(dāng)這個(gè)進(jìn)程啟動時(shí),它創(chuàng)建一個(gè)人工重置的未通知狀態(tài)的事件,并且將句柄保存在一個(gè)全局變量中。這使得該進(jìn)程中的其他線程能夠非常容易地訪問同一個(gè)事件對象?,F(xiàn)在3個(gè)線程已經(jīng)產(chǎn)生。這些線程要等待文件的內(nèi)容讀入內(nèi)存,然后每個(gè)線程都要訪問它的數(shù)據(jù)。一個(gè)線程進(jìn)行單詞計(jì)數(shù),另一個(gè)線程運(yùn)行拼寫檢查器,第三個(gè)線程運(yùn)行語法檢查器。這3個(gè)線程函數(shù)的代碼的開始部分都相同,每個(gè)函數(shù)都調(diào)用Wa i t F o r S i n g l e O b j e c t,這將使線程暫停運(yùn)行,直到文件的內(nèi)容由主線程讀入內(nèi)存為止。

一旦主線程將數(shù)據(jù)準(zhǔn)備好,它就調(diào)用S e t E v e n t,給事件發(fā)出通知信號。這時(shí),系統(tǒng)就使所有這3個(gè)輔助線程進(jìn)入可調(diào)度狀態(tài),它們都獲得了C P U時(shí)間,并且可以訪問內(nèi)存塊。注意,這3個(gè)線程都以只讀方式訪問內(nèi)存。這就是所有3個(gè)線程能夠同時(shí)運(yùn)行的唯一原因。還要注意,如何計(jì)算機(jī)上配有多個(gè)C P U,那么所有3個(gè)線程都能夠真正地同時(shí)運(yùn)行,從而可以在很短的時(shí)間內(nèi)完成大量的操作。

 

 

       如果該事件設(shè)置為自動重置,則三個(gè)線程都不能同時(shí)運(yùn)行了,只有一個(gè)會被調(diào)度。其它兩個(gè)就要等待,直到被調(diào)度的調(diào)用完畢,線程的代碼也要加一句如下所示:

DWORD WINAPI WordCount(PVOID pvParam)
{
   //Wait until the file's data is in memory.
   WaitForSingleObject(g_hEvent, INFINITE);
 
   //Access the memory block.
   ...
   SetEvent(g_hEvent);  //這句是把事件改為已通知狀態(tài),可以讓其它使用該事件的線程被調(diào)度了。
   return(0);
}
       其它的線程代碼也如上所示。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
CreateEvent的用法
C++語言筆記
Windows線程同步與互斥技術(shù)總結(jié)
CEvent類祥解
windows 事件
CEvent類及常用函數(shù)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服