• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            twzheng's cppblog

            『站在風(fēng)口浪尖緊握住鼠標(biāo)旋轉(zhuǎn)!』 http://www.cnblogs.com/twzheng

              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              136 隨筆 :: 78 文章 :: 353 評(píng)論 :: 0 Trackbacks
            Visual C++線(xiàn)程同步技術(shù)剖析
            摘自:天極網(wǎng)
            作者:中國(guó)電波傳播研究所 郎銳

                摘要: 多線(xiàn)程同步技術(shù)是計(jì)算機(jī)軟件開(kāi)發(fā)的重要技術(shù),本文對(duì)多線(xiàn)程的各種同步技術(shù)的原理和實(shí)現(xiàn)進(jìn)行了初步探討。

              關(guān)鍵詞: VC++6.0; 線(xiàn)程同步;臨界區(qū);事件;互斥;信號(hào)量;

              閱讀目錄:

              使線(xiàn)程同步
              臨界區(qū)
              管理事件內(nèi)核對(duì)象
              信號(hào)量?jī)?nèi)核對(duì)象
              互斥內(nèi)核對(duì)象
              小結(jié)

              正文

              使線(xiàn)程同步

              在程序中使用多線(xiàn)程時(shí),一般很少有多個(gè)線(xiàn)程能在其生命期內(nèi)進(jìn)行完全獨(dú)立的操作。更多的情況是一些線(xiàn)程進(jìn)行某些處理操作,而其他的線(xiàn)程必須對(duì)其處理結(jié)果進(jìn)行了解。正常情況下對(duì)這種處理結(jié)果的了解應(yīng)當(dāng)在其處理任務(wù)完成后進(jìn)行。

              如果不采取適當(dāng)?shù)拇胧渌€(xiàn)程往往會(huì)在線(xiàn)程處理任務(wù)結(jié)束前就去訪(fǎng)問(wèn)處理結(jié)果,這就很有可能得到有關(guān)處理結(jié)果的錯(cuò)誤了解。例如,多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)同一個(gè)全局變量,如果都是讀取操作,則不會(huì)出現(xiàn)問(wèn)題。如果一個(gè)線(xiàn)程負(fù)責(zé)改變此變量的值,而其他線(xiàn)程負(fù)責(zé)同時(shí)讀取變量?jī)?nèi)容,則不能保證讀取到的數(shù)據(jù)是經(jīng)過(guò)寫(xiě)線(xiàn)程修改后的。

              為了確保讀線(xiàn)程讀取到的是經(jīng)過(guò)修改的變量,就必須在向變量寫(xiě)入數(shù)據(jù)時(shí)禁止其他線(xiàn)程對(duì)其的任何訪(fǎng)問(wèn),直至賦值過(guò)程結(jié)束后再解除對(duì)其他線(xiàn)程的訪(fǎng)問(wèn)限制。象這種保證線(xiàn)程能了解其他線(xiàn)程任務(wù)處理結(jié)束后的處理結(jié)果而采取的保護(hù)措施即為線(xiàn)程同步。

              線(xiàn)程同步是一個(gè)非常大的話(huà)題,包括方方面面的內(nèi)容。從大的方面講,線(xiàn)程的同步可分用戶(hù)模式的線(xiàn)程同步和內(nèi)核對(duì)象的線(xiàn)程同步兩大類(lèi)。用戶(hù)模式中線(xiàn)程的同步方法主要有原子訪(fǎng)問(wèn)和臨界區(qū)等方法。其特點(diǎn)是同步速度特別快,適合于對(duì)線(xiàn)程運(yùn)行速度有嚴(yán)格要求的場(chǎng)合。

              內(nèi)核對(duì)象的線(xiàn)程同步則主要由事件、等待定時(shí)器、信號(hào)量以及信號(hào)燈等內(nèi)核對(duì)象構(gòu)成。由于這種同步機(jī)制使用了內(nèi)核對(duì)象,使用時(shí)必須將線(xiàn)程從用戶(hù)模式切換到內(nèi)核模式,而這種轉(zhuǎn)換一般要耗費(fèi)近千個(gè)CPU周期,因此同步速度較慢,但在適用性上卻要遠(yuǎn)優(yōu)于用戶(hù)模式的線(xiàn)程同步方式。
              臨界區(qū)

              臨界區(qū)(Critical Section)是一段獨(dú)占對(duì)某些共享資源訪(fǎng)問(wèn)的代碼,在任意時(shí)刻只允許一個(gè)線(xiàn)程對(duì)共享資源進(jìn)行訪(fǎng)問(wèn)。如果有多個(gè)線(xiàn)程試圖同時(shí)訪(fǎng)問(wèn)臨界區(qū),那么在有一個(gè)線(xiàn)程進(jìn)入后其他所有試圖訪(fǎng)問(wèn)此臨界區(qū)的線(xiàn)程將被掛起,并一直持續(xù)到進(jìn)入臨界區(qū)的線(xiàn)程離開(kāi)。臨界區(qū)在被釋放后,其他線(xiàn)程可以繼續(xù)搶占,并以此達(dá)到用原子方式操作共享資源的目的。

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

            圖1 使用臨界區(qū)保持線(xiàn)程同步
              下面通過(guò)一段代碼展示了臨界區(qū)在保護(hù)多線(xiàn)程訪(fǎng)問(wèn)的共享資源中的作用。通過(guò)兩個(gè)線(xiàn)程來(lái)分別對(duì)全局變量g_cArray[10]進(jìn)行寫(xiě)入操作,用臨界區(qū)結(jié)構(gòu)對(duì)象g_cs來(lái)保持線(xiàn)程的同步,并在開(kāi)啟線(xiàn)程前對(duì)其進(jìn)行初始化。為了使實(shí)驗(yàn)效果更加明顯,體現(xiàn)出臨界區(qū)的作用,在線(xiàn)程函數(shù)對(duì)共享資源g_cArray[10]的寫(xiě)入時(shí),以Sleep()函數(shù)延遲1毫秒,使其他線(xiàn)程同其搶占CPU的可能性增大。如果不使用臨界區(qū)對(duì)其進(jìn)行保護(hù),則共享資源數(shù)據(jù)將被破壞(參見(jiàn)圖1(a)所示計(jì)算結(jié)果),而使用臨界區(qū)對(duì)線(xiàn)程保持同步后則可以得到正確的結(jié)果(參見(jiàn)圖1(b)所示計(jì)算結(jié)果)。代碼實(shí)現(xiàn)清單附下:

            // 臨界區(qū)結(jié)構(gòu)對(duì)象
            CRITICAL_SECTION g_cs;
            // 共享資源 
            char g_cArray[10];
            UINT ThreadProc10(LPVOID pParam)
            {
             
            // 進(jìn)入臨界區(qū)
             EnterCriticalSection(&g_cs);
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[i] 
            = 'a';
              Sleep(
            1);
             }

             
            // 離開(kāi)臨界區(qū)
             LeaveCriticalSection(&g_cs);
             
            return 0;
            }

            UINT ThreadProc11(LPVOID pParam)
            {
             
            // 進(jìn)入臨界區(qū)
             EnterCriticalSection(&g_cs);
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[
            10 - i - 1= 'b';
              Sleep(
            1);
             }

             
            // 離開(kāi)臨界區(qū)
             LeaveCriticalSection(&g_cs);
             
            return 0;
            }

            ……
            void CSample08View::OnCriticalSection() 
            {
             
            // 初始化臨界區(qū)
             InitializeCriticalSection(&g_cs);
             
            // 啟動(dòng)線(xiàn)程
             AfxBeginThread(ThreadProc10, NULL);
             AfxBeginThread(ThreadProc11, NULL);
             
            // 等待計(jì)算完畢
             Sleep(300);
             
            // 報(bào)告計(jì)算結(jié)果
             CString sResult = CString(g_cArray);
             AfxMessageBox(sResult);
            }


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

              MFC為臨界區(qū)提供有一個(gè)CCriticalSection類(lèi),使用該類(lèi)進(jìn)行線(xiàn)程同步處理是非常簡(jiǎn)單的,只需在線(xiàn)程函數(shù)中用CCriticalSection類(lèi)成員函數(shù)Lock()和UnLock()標(biāo)定出被保護(hù)代碼片段即可。對(duì)于上述代碼,可通過(guò)CCriticalSection類(lèi)將其改寫(xiě)如下:

            // MFC臨界區(qū)類(lèi)對(duì)象
            CCriticalSection g_clsCriticalSection;
            // 共享資源 
            char g_cArray[10];
            UINT ThreadProc20(LPVOID pParam)
            {
             
            // 進(jìn)入臨界區(qū)
             g_clsCriticalSection.Lock();
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[i] 
            = 'a';
              Sleep(
            1);
             }

             
            // 離開(kāi)臨界區(qū)
             g_clsCriticalSection.Unlock();
             
            return 0;
            }

            UINT ThreadProc21(LPVOID pParam)
            {
             
            // 進(jìn)入臨界區(qū)
             g_clsCriticalSection.Lock();
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[
            10 - i - 1= 'b';
              Sleep(
            1);
             }

             
            // 離開(kāi)臨界區(qū)
             g_clsCriticalSection.Unlock();
             
            return 0;
            }

            ……
            void CSample08View::OnCriticalSectionMfc() 
            {
             
            // 啟動(dòng)線(xiàn)程
             AfxBeginThread(ThreadProc20, NULL);
             AfxBeginThread(ThreadProc21, NULL);
             
            // 等待計(jì)算完畢
             Sleep(300);
             
            // 報(bào)告計(jì)算結(jié)果
             CString sResult = CString(g_cArray);
             AfxMessageBox(sResult);
            }


              管理事件內(nèi)核對(duì)象

              在前面講述線(xiàn)程通信時(shí)曾使用過(guò)事件內(nèi)核對(duì)象來(lái)進(jìn)行線(xiàn)程間的通信,除此之外,事件內(nèi)核對(duì)象也可以通過(guò)通知操作的方式來(lái)保持線(xiàn)程的同步。對(duì)于前面那段使用臨界區(qū)保持線(xiàn)程同步的代碼可用事件對(duì)象的線(xiàn)程同步方法改寫(xiě)如下:

            // 事件句柄
            HANDLE hEvent = NULL;
            // 共享資源 
            char g_cArray[10];
            ……
            UINT ThreadProc12(LPVOID pParam)
            {
             
            // 等待事件置位
             WaitForSingleObject(hEvent, INFINITE);
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[i] 
            = 'a';
              Sleep(
            1);
             }

             
            // 處理完成后即將事件對(duì)象置位
             SetEvent(hEvent);
             
            return 0;
            }

            UINT ThreadProc13(LPVOID pParam)
            {
             
            // 等待事件置位
             WaitForSingleObject(hEvent, INFINITE);
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[
            10 - i - 1= 'b';
              Sleep(
            1);
             }

             
            // 處理完成后即將事件對(duì)象置位
             SetEvent(hEvent);
             
            return 0;
            }

            ……
            void CSample08View::OnEvent() 
            {
             
            // 創(chuàng)建事件
             hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
             
            // 事件置位
             SetEvent(hEvent);
             
            // 啟動(dòng)線(xiàn)程
             AfxBeginThread(ThreadProc12, NULL);
             AfxBeginThread(ThreadProc13, NULL);
             
            // 等待計(jì)算完畢
             Sleep(300);
             
            // 報(bào)告計(jì)算結(jié)果
             CString sResult = CString(g_cArray);
             AfxMessageBox(sResult);
            }


              在創(chuàng)建線(xiàn)程前,首先創(chuàng)建一個(gè)可以自動(dòng)復(fù)位的事件內(nèi)核對(duì)象hEvent,而線(xiàn)程函數(shù)則通過(guò)WaitForSingleObject()等待函數(shù)無(wú)限等待hEvent的置位,只有在事件置位時(shí)WaitForSingleObject()才會(huì)返回,被保護(hù)的代碼將得以執(zhí)行。對(duì)于以自動(dòng)復(fù)位方式創(chuàng)建的事件對(duì)象,在其置位后一被WaitForSingleObject()等待到就會(huì)立即復(fù)位,也就是說(shuō)在執(zhí)行ThreadProc12()中的受保護(hù)代碼時(shí),事件對(duì)象已經(jīng)是復(fù)位狀態(tài)的,這時(shí)即使有ThreadProc13()對(duì)CPU的搶占,也會(huì)由于WaitForSingleObject()沒(méi)有hEvent的置位而不能繼續(xù)執(zhí)行,也就沒(méi)有可能破壞受保護(hù)的共享資源。在ThreadProc12()中的處理完成后可以通過(guò)SetEvent()對(duì)hEvent的置位而允許ThreadProc13()對(duì)共享資源g_cArray的處理。這里SetEvent()所起的作用可以看作是對(duì)某項(xiàng)特定任務(wù)完成的通知。

              使用臨界區(qū)只能同步同一進(jìn)程中的線(xiàn)程,而使用事件內(nèi)核對(duì)象則可以對(duì)進(jìn)程外的線(xiàn)程進(jìn)行同步,其前提是得到對(duì)此事件對(duì)象的訪(fǎng)問(wèn)權(quán)。可以通過(guò)OpenEvent()函數(shù)獲取得到,其函數(shù)原型為:

            HANDLE OpenEvent(
             DWORD dwDesiredAccess, 
            // 訪(fǎng)問(wèn)標(biāo)志
             BOOL bInheritHandle, // 繼承標(biāo)志
             LPCTSTR lpName // 指向事件對(duì)象名的指針
            );


              如果事件對(duì)象已創(chuàng)建(在創(chuàng)建事件時(shí)需要指定事件名),函數(shù)將返回指定事件的句柄。對(duì)于那些在創(chuàng)建事件時(shí)沒(méi)有指定事件名的事件內(nèi)核對(duì)象,可以通過(guò)使用內(nèi)核對(duì)象的繼承性或是調(diào)用DuplicateHandle()函數(shù)來(lái)調(diào)用CreateEvent()以獲得對(duì)指定事件對(duì)象的訪(fǎng)問(wèn)權(quán)。在獲取到訪(fǎng)問(wèn)權(quán)后所進(jìn)行的同步操作與在同一個(gè)進(jìn)程中所進(jìn)行的線(xiàn)程同步操作是一樣的。

              如果需要在一個(gè)線(xiàn)程中等待多個(gè)事件,則用WaitForMultipleObjects()來(lái)等待。WaitForMultipleObjects()與WaitForSingleObject()類(lèi)似,同時(shí)監(jiān)視位于句柄數(shù)組中的所有句柄。這些被監(jiān)視對(duì)象的句柄享有平等的優(yōu)先權(quán),任何一個(gè)句柄都不可能比其他句柄具有更高的優(yōu)先權(quán)。WaitForMultipleObjects()的函數(shù)原型為:

            DWORD WaitForMultipleObjects(
             DWORD nCount, 
            // 等待句柄數(shù)
             CONST HANDLE *lpHandles, // 句柄數(shù)組首地址
             BOOL fWaitAll, // 等待標(biāo)志
             DWORD dwMilliseconds // 等待時(shí)間間隔
            );


              參數(shù)nCount指定了要等待的內(nèi)核對(duì)象的數(shù)目,存放這些內(nèi)核對(duì)象的數(shù)組由lpHandles來(lái)指向。fWaitAll對(duì)指定的這nCount個(gè)內(nèi)核對(duì)象的兩種等待方式進(jìn)行了指定,為T(mén)RUE時(shí)當(dāng)所有對(duì)象都被通知時(shí)函數(shù)才會(huì)返回,為FALSE則只要其中任何一個(gè)得到通知就可以返回。dwMilliseconds在這里的作用與在WaitForSingleObject()中的作用是完全一致的。如果等待超時(shí),函數(shù)將返回WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某個(gè)值,則說(shuō)明所有指定對(duì)象的狀態(tài)均為已通知狀態(tài)(當(dāng)fWaitAll為T(mén)RUE時(shí))或是用以減去WAIT_OBJECT_0而得到發(fā)生通知的對(duì)象的索引(當(dāng)fWaitAll為FALSE時(shí))。如果返回值在WAIT_ABANDONED_0與WAIT_ABANDONED_0+nCount-1之間,則表示所有指定對(duì)象的狀態(tài)均為已通知,且其中至少有一個(gè)對(duì)象是被丟棄的互斥對(duì)象(當(dāng)fWaitAll為T(mén)RUE時(shí)),或是用以減去WAIT_OBJECT_0表示一個(gè)等待正常結(jié)束的互斥對(duì)象的索引(當(dāng)fWaitAll為FALSE時(shí))。 下面給出的代碼主要展示了對(duì)WaitForMultipleObjects()函數(shù)的使用。通過(guò)對(duì)兩個(gè)事件內(nèi)核對(duì)象的等待來(lái)控制線(xiàn)程任務(wù)的執(zhí)行與中途退出:

            // 存放事件句柄的數(shù)組
            HANDLE hEvents[2];
            UINT ThreadProc14(LPVOID pParam)

             
            // 等待開(kāi)啟事件
             DWORD dwRet1 = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
             
            // 如果開(kāi)啟事件到達(dá)則線(xiàn)程開(kāi)始執(zhí)行任務(wù)
             if (dwRet1 == WAIT_OBJECT_0)
             
            {
              AfxMessageBox(
            "線(xiàn)程開(kāi)始工作!");
              
            while (true)
              
            {
               
            for (int i = 0; i < 10000; i++);
               
            // 在任務(wù)處理過(guò)程中等待結(jié)束事件 
               DWORD dwRet2 = WaitForMultipleObjects(2, hEvents, FALSE, 0);
               
            // 如果結(jié)束事件置位則立即終止任務(wù)的執(zhí)行
               if (dwRet2 == WAIT_OBJECT_0 + 1)
                
            break;
              }

             }

             AfxMessageBox(
            "線(xiàn)程退出!");
             
            return 0;
            }

            ……
            void CSample08View::OnStartEvent() 
            {
             
            // 創(chuàng)建線(xiàn)程
             for (int i = 0; i < 2; i++)
              hEvents[i] 
            = CreateEvent(NULL, FALSE, FALSE, NULL);
              
            // 開(kāi)啟線(xiàn)程
              AfxBeginThread(ThreadProc14, NULL);
              
            // 設(shè)置事件0(開(kāi)啟事件)
              SetEvent(hEvents[0]);
            }

            void CSample08View::OnEndevent() 
            {
             
            // 設(shè)置事件1(結(jié)束事件)
             SetEvent(hEvents[1]);
            }


              MFC為事件相關(guān)處理也提供了一個(gè)CEvent類(lèi),共包含有除構(gòu)造函數(shù)外的4個(gè)成員函數(shù)PulseEvent()、ResetEvent()、SetEvent()和UnLock()。在功能上分別相當(dāng)與Win32 API的PulseEvent()、ResetEvent()、SetEvent()和CloseHandle()等函數(shù)。而構(gòu)造函數(shù)則履行了原CreateEvent()函數(shù)創(chuàng)建事件對(duì)象的職責(zé),其函數(shù)原型為:

            CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );


              按照此缺省設(shè)置將創(chuàng)建一個(gè)自動(dòng)復(fù)位、初始狀態(tài)為復(fù)位狀態(tài)的沒(méi)有名字的事件對(duì)象。封裝后的CEvent類(lèi)使用起來(lái)更加方便,圖2即展示了CEvent類(lèi)對(duì)A、B兩線(xiàn)程的同步過(guò)程:


            圖2 CEvent類(lèi)對(duì)線(xiàn)程的同步過(guò)程示意

              B線(xiàn)程在執(zhí)行到CEvent類(lèi)成員函數(shù)Lock()時(shí)將會(huì)發(fā)生阻塞,而A線(xiàn)程此時(shí)則可以在沒(méi)有B線(xiàn)程干擾的情況下對(duì)共享資源進(jìn)行處理,并在處理完成后通過(guò)成員函數(shù)SetEvent()向B發(fā)出事件,使其被釋放,得以對(duì)A先前已處理完畢的共享資源進(jìn)行操作。可見(jiàn),使用CEvent類(lèi)對(duì)線(xiàn)程的同步方法與通過(guò)API函數(shù)進(jìn)行線(xiàn)程同步的處理方法是基本一致的。前面的API處理代碼可用CEvent類(lèi)將其改寫(xiě)為:

            // MFC事件類(lèi)對(duì)象
            CEvent g_clsEvent;
            UINT ThreadProc22(LPVOID pParam)
            {
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[i] 
            = 'a';
              Sleep(
            1);
             }

             
            // 事件置位
             g_clsEvent.SetEvent();
             
            return 0;
            }

            UINT ThreadProc23(LPVOID pParam)
            {
             
            // 等待事件
             g_clsEvent.Lock();
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[
            10 - i - 1= 'b';
              Sleep(
            1);
             }

             
            return 0;
            }

            ……
            void CSample08View::OnEventMfc() 
            {
             
            // 啟動(dòng)線(xiàn)程
             AfxBeginThread(ThreadProc22, NULL);
             AfxBeginThread(ThreadProc23, NULL);
             
            // 等待計(jì)算完畢
             Sleep(300);
             
            // 報(bào)告計(jì)算結(jié)果
             CString sResult = CString(g_cArray);
             AfxMessageBox(sResult);
            }


              信號(hào)量?jī)?nèi)核對(duì)象

              信號(hào)量(Semaphore)內(nèi)核對(duì)象對(duì)線(xiàn)程的同步方式與前面幾種方法不同,它允許多個(gè)線(xiàn)程在同一時(shí)刻訪(fǎng)問(wèn)同一資源,但是需要限制在同一時(shí)刻訪(fǎng)問(wèn)此資源的最大線(xiàn)程數(shù)目。在用CreateSemaphore()創(chuàng)建信號(hào)量時(shí)即要同時(shí)指出允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。一般是將當(dāng)前可用資源計(jì)數(shù)設(shè)置為最大資源計(jì)數(shù),每增加一個(gè)線(xiàn)程對(duì)共享資源的訪(fǎng)問(wèn),當(dāng)前可用資源計(jì)數(shù)就會(huì)減1,只要當(dāng)前可用資源計(jì)數(shù)是大于0的,就可以發(fā)出信號(hào)量信號(hào)。但是當(dāng)前可用計(jì)數(shù)減小到0時(shí)則說(shuō)明當(dāng)前占用資源的線(xiàn)程數(shù)已經(jīng)達(dá)到了所允許的最大數(shù)目,不能在允許其他線(xiàn)程的進(jìn)入,此時(shí)的信號(hào)量信號(hào)將無(wú)法發(fā)出。線(xiàn)程在處理完共享資源后,應(yīng)在離開(kāi)的同時(shí)通過(guò)ReleaseSemaphore()函數(shù)將當(dāng)前可用資源計(jì)數(shù)加1。在任何時(shí)候當(dāng)前可用資源計(jì)數(shù)決不可能大于最大資源計(jì)數(shù)。


            圖3 使用信號(hào)量對(duì)象控制資源

              下面結(jié)合圖例3來(lái)演示信號(hào)量對(duì)象對(duì)資源的控制。在圖3中,以箭頭和白色箭頭表示共享資源所允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。初始如圖(a)所示,最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)均為4,此后每增加一個(gè)對(duì)資源進(jìn)行訪(fǎng)問(wèn)的線(xiàn)程(用黑色箭頭表示)當(dāng)前資源計(jì)數(shù)就會(huì)相應(yīng)減1,圖(b)即表示的在3個(gè)線(xiàn)程對(duì)共享資源進(jìn)行訪(fǎng)問(wèn)時(shí)的狀態(tài)。當(dāng)進(jìn)入線(xiàn)程數(shù)達(dá)到4個(gè)時(shí),將如圖(c)所示,此時(shí)已達(dá)到最大資源計(jì)數(shù),而當(dāng)前可用資源計(jì)數(shù)也已減到0,其他線(xiàn)程無(wú)法對(duì)共享資源進(jìn)行訪(fǎng)問(wèn)。在當(dāng)前占有資源的線(xiàn)程處理完畢而退出后,將會(huì)釋放出空間,圖(d)已有兩個(gè)線(xiàn)程退出對(duì)資源的占有,當(dāng)前可用計(jì)數(shù)為2,可以再允許2個(gè)線(xiàn)程進(jìn)入到對(duì)資源的處理。可以看出,信號(hào)量是通過(guò)計(jì)數(shù)來(lái)對(duì)線(xiàn)程訪(fǎng)問(wèn)資源進(jìn)行控制的,而實(shí)際上信號(hào)量確實(shí)也被稱(chēng)作Dijkstra計(jì)數(shù)器。

              使用信號(hào)量?jī)?nèi)核對(duì)象進(jìn)行線(xiàn)程同步主要會(huì)用到CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()等函數(shù)。其中,CreateSemaphore()用來(lái)創(chuàng)建一個(gè)信號(hào)量?jī)?nèi)核對(duì)象,其函數(shù)原型為:

            HANDLE CreateSemaphore(
             LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, 
            // 安全屬性指針
             LONG lInitialCount, // 初始計(jì)數(shù)
             LONG lMaximumCount, // 最大計(jì)數(shù)
             LPCTSTR lpName // 對(duì)象名指針
            );


              參數(shù)lMaximumCount是一個(gè)有符號(hào)32位值,定義了允許的最大資源計(jì)數(shù),最大取值不能超過(guò)4294967295。lpName參數(shù)可以為創(chuàng)建的信號(hào)量定義一個(gè)名字,由于其創(chuàng)建的是一個(gè)內(nèi)核對(duì)象,因此在其他進(jìn)程中可以通過(guò)該名字而得到此信號(hào)量。OpenSemaphore()函數(shù)即可用來(lái)根據(jù)信號(hào)量名打開(kāi)在其他進(jìn)程中創(chuàng)建的信號(hào)量,函數(shù)原型如下:

            HANDLE OpenSemaphore(
             DWORD dwDesiredAccess, 
            // 訪(fǎng)問(wèn)標(biāo)志
             BOOL bInheritHandle, // 繼承標(biāo)志
             LPCTSTR lpName // 信號(hào)量名
            );


              在線(xiàn)程離開(kāi)對(duì)共享資源的處理時(shí),必須通過(guò)ReleaseSemaphore()來(lái)增加當(dāng)前可用資源計(jì)數(shù)。否則將會(huì)出現(xiàn)當(dāng)前正在處理共享資源的實(shí)際線(xiàn)程數(shù)并沒(méi)有達(dá)到要限制的數(shù)值,而其他線(xiàn)程卻因?yàn)楫?dāng)前可用資源計(jì)數(shù)為0而仍無(wú)法進(jìn)入的情況。ReleaseSemaphore()的函數(shù)原型為:

            BOOL ReleaseSemaphore(
             HANDLE hSemaphore, 
            // 信號(hào)量句柄
             LONG lReleaseCount, // 計(jì)數(shù)遞增數(shù)量
             LPLONG lpPreviousCount // 先前計(jì)數(shù)
            );


              該函數(shù)將lReleaseCount中的值添加給信號(hào)量的當(dāng)前資源計(jì)數(shù),一般將lReleaseCount設(shè)置為1,如果需要也可以設(shè)置其他的值。WaitForSingleObject()和WaitForMultipleObjects()主要用在試圖進(jìn)入共享資源的線(xiàn)程函數(shù)入口處,主要用來(lái)判斷信號(hào)量的當(dāng)前可用資源計(jì)數(shù)是否允許本線(xiàn)程的進(jìn)入。只有在當(dāng)前可用資源計(jì)數(shù)值大于0時(shí),被監(jiān)視的信號(hào)量?jī)?nèi)核對(duì)象才會(huì)得到通知。

              信號(hào)量的使用特點(diǎn)使其更適用于對(duì)Socket(套接字)程序中線(xiàn)程的同步。例如,網(wǎng)絡(luò)上的HTTP服務(wù)器要對(duì)同一時(shí)間內(nèi)訪(fǎng)問(wèn)同一頁(yè)面的用戶(hù)數(shù)加以限制,這時(shí)可以為沒(méi)一個(gè)用戶(hù)對(duì)服務(wù)器的頁(yè)面請(qǐng)求設(shè)置一個(gè)線(xiàn)程,而頁(yè)面則是待保護(hù)的共享資源,通過(guò)使用信號(hào)量對(duì)線(xiàn)程的同步作用可以確保在任一時(shí)刻無(wú)論有多少用戶(hù)對(duì)某一頁(yè)面進(jìn)行訪(fǎng)問(wèn),只有不大于設(shè)定的最大用戶(hù)數(shù)目的線(xiàn)程能夠進(jìn)行訪(fǎng)問(wèn),而其他的訪(fǎng)問(wèn)企圖則被掛起,只有在有用戶(hù)退出對(duì)此頁(yè)面的訪(fǎng)問(wèn)后才有可能進(jìn)入。下面給出的示例代碼即展示了類(lèi)似的處理過(guò)程:

            // 信號(hào)量對(duì)象句柄
            HANDLE hSemaphore;
            UINT ThreadProc15(LPVOID pParam)

             
            // 試圖進(jìn)入信號(hào)量關(guān)口
             WaitForSingleObject(hSemaphore, INFINITE);
             
            // 線(xiàn)程任務(wù)處理
             AfxMessageBox("線(xiàn)程一正在執(zhí)行!");
             
            // 釋放信號(hào)量計(jì)數(shù)
             ReleaseSemaphore(hSemaphore, 1, NULL);
             
            return 0;
            }

            UINT ThreadProc16(LPVOID pParam)

             
            // 試圖進(jìn)入信號(hào)量關(guān)口
             WaitForSingleObject(hSemaphore, INFINITE);
             
            // 線(xiàn)程任務(wù)處理
             AfxMessageBox("線(xiàn)程二正在執(zhí)行!");
             
            // 釋放信號(hào)量計(jì)數(shù)
             ReleaseSemaphore(hSemaphore, 1, NULL);
             
            return 0;
            }

            UINT ThreadProc17(LPVOID pParam)

             
            // 試圖進(jìn)入信號(hào)量關(guān)口
             WaitForSingleObject(hSemaphore, INFINITE);
             
            // 線(xiàn)程任務(wù)處理
             AfxMessageBox("線(xiàn)程三正在執(zhí)行!");
             
            // 釋放信號(hào)量計(jì)數(shù)
             ReleaseSemaphore(hSemaphore, 1, NULL);
             
            return 0;
            }

            ……
            void CSample08View::OnSemaphore() 
            {
             
            // 創(chuàng)建信號(hào)量對(duì)象
             hSemaphore = CreateSemaphore(NULL, 22, NULL);
             
            // 開(kāi)啟線(xiàn)程
             AfxBeginThread(ThreadProc15, NULL);
             AfxBeginThread(ThreadProc16, NULL);
             AfxBeginThread(ThreadProc17, NULL);
            }



            圖4 開(kāi)始進(jìn)入的兩個(gè)線(xiàn)程


            圖5 線(xiàn)程二退出后線(xiàn)程三才得以進(jìn)入

              上述代碼在開(kāi)啟線(xiàn)程前首先創(chuàng)建了一個(gè)初始計(jì)數(shù)和最大資源計(jì)數(shù)均為2的信號(hào)量對(duì)象hSemaphore。即在同一時(shí)刻只允許2個(gè)線(xiàn)程進(jìn)入由hSemaphore保護(hù)的共享資源。隨后開(kāi)啟的三個(gè)線(xiàn)程均試圖訪(fǎng)問(wèn)此共享資源,在前兩個(gè)線(xiàn)程試圖訪(fǎng)問(wèn)共享資源時(shí),由于hSemaphore的當(dāng)前可用資源計(jì)數(shù)分別為2和1,此時(shí)的hSemaphore是可以得到通知的,也就是說(shuō)位于線(xiàn)程入口處的WaitForSingleObject()將立即返回,而在前兩個(gè)線(xiàn)程進(jìn)入到保護(hù)區(qū)域后,hSemaphore的當(dāng)前資源計(jì)數(shù)減少到0,hSemaphore將不再得到通知,WaitForSingleObject()將線(xiàn)程掛起。直到此前進(jìn)入到保護(hù)區(qū)的線(xiàn)程退出后才能得以進(jìn)入。圖4和圖5為上述代脈的運(yùn)行結(jié)果。從實(shí)驗(yàn)結(jié)果可以看出,信號(hào)量始終保持了同一時(shí)刻不超過(guò)2個(gè)線(xiàn)程的進(jìn)入。

              在MFC中,通過(guò)CSemaphore類(lèi)對(duì)信號(hào)量作了表述。該類(lèi)只具有一個(gè)構(gòu)造函數(shù),可以構(gòu)造一個(gè)信號(hào)量對(duì)象,并對(duì)初始資源計(jì)數(shù)、最大資源計(jì)數(shù)、對(duì)象名和安全屬性等進(jìn)行初始化,其原型如下:

            CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );


              在構(gòu)造了CSemaphore類(lèi)對(duì)象后,任何一個(gè)訪(fǎng)問(wèn)受保護(hù)共享資源的線(xiàn)程都必須通過(guò)CSemaphore從父類(lèi)CSyncObject類(lèi)繼承得到的Lock()和UnLock()成員函數(shù)來(lái)訪(fǎng)問(wèn)或釋放CSemaphore對(duì)象。與前面介紹的幾種通過(guò)MFC類(lèi)保持線(xiàn)程同步的方法類(lèi)似,通過(guò)CSemaphore類(lèi)也可以將前面的線(xiàn)程同步代碼進(jìn)行改寫(xiě),這兩種使用信號(hào)量的線(xiàn)程同步方法無(wú)論是在實(shí)現(xiàn)原理上還是從實(shí)現(xiàn)結(jié)果上都是完全一致的。下面給出經(jīng)MFC改寫(xiě)后的信號(hào)量線(xiàn)程同步代碼:

            // MFC信號(hào)量類(lèi)對(duì)象
            CSemaphore g_clsSemaphore(22);
            UINT ThreadProc24(LPVOID pParam)

             
            // 試圖進(jìn)入信號(hào)量關(guān)口
             g_clsSemaphore.Lock();
             
            // 線(xiàn)程任務(wù)處理
             AfxMessageBox("線(xiàn)程一正在執(zhí)行!");
             
            // 釋放信號(hào)量計(jì)數(shù)
             g_clsSemaphore.Unlock();
             
            return 0;
            }

            UINT ThreadProc25(LPVOID pParam)
            {
             
            // 試圖進(jìn)入信號(hào)量關(guān)口
             g_clsSemaphore.Lock();
             
            // 線(xiàn)程任務(wù)處理
             AfxMessageBox("線(xiàn)程二正在執(zhí)行!");
             
            // 釋放信號(hào)量計(jì)數(shù)
             g_clsSemaphore.Unlock();
             
            return 0;
            }

            UINT ThreadProc26(LPVOID pParam)
            {
             
            // 試圖進(jìn)入信號(hào)量關(guān)口
             g_clsSemaphore.Lock();
             
            // 線(xiàn)程任務(wù)處理
             AfxMessageBox("線(xiàn)程三正在執(zhí)行!");
             
            // 釋放信號(hào)量計(jì)數(shù)
             g_clsSemaphore.Unlock();
             
            return 0;
            }

            ……
            void CSample08View::OnSemaphoreMfc() 
            {
             
            // 開(kāi)啟線(xiàn)程
             AfxBeginThread(ThreadProc24, NULL);
             AfxBeginThread(ThreadProc25, NULL);
             AfxBeginThread(ThreadProc26, NULL);
            }


              互斥內(nèi)核對(duì)象

              互斥(Mutex)是一種用途非常廣泛的內(nèi)核對(duì)象。能夠保證多個(gè)線(xiàn)程對(duì)同一共享資源的互斥訪(fǎng)問(wèn)。同臨界區(qū)有些類(lèi)似,只有擁有互斥對(duì)象的線(xiàn)程才具有訪(fǎng)問(wèn)資源的權(quán)限,由于互斥對(duì)象只有一個(gè),因此就決定了任何情況下此共享資源都不會(huì)同時(shí)被多個(gè)線(xiàn)程所訪(fǎng)問(wèn)。當(dāng)前占據(jù)資源的線(xiàn)程在任務(wù)處理完后應(yīng)將擁有的互斥對(duì)象交出,以便其他線(xiàn)程在獲得后得以訪(fǎng)問(wèn)資源。與其他幾種內(nèi)核對(duì)象不同,互斥對(duì)象在操作系統(tǒng)中擁有特殊代碼,并由操作系統(tǒng)來(lái)管理,操作系統(tǒng)甚至還允許其進(jìn)行一些其他內(nèi)核對(duì)象所不能進(jìn)行的非常規(guī)操作。為便于理解,可參照?qǐng)D6給出的互斥內(nèi)核對(duì)象的工作模型:


            圖6 使用互斥內(nèi)核對(duì)象對(duì)共享資源的保護(hù)

              圖(a)中的箭頭為要訪(fǎng)問(wèn)資源(矩形框)的線(xiàn)程,但只有第二個(gè)線(xiàn)程擁有互斥對(duì)象(黑點(diǎn))并得以進(jìn)入到共享資源,而其他線(xiàn)程則會(huì)被排斥在外(如圖(b)所示)。當(dāng)此線(xiàn)程處理完共享資源并準(zhǔn)備離開(kāi)此區(qū)域時(shí)將把其所擁有的互斥對(duì)象交出(如圖(c)所示),其他任何一個(gè)試圖訪(fǎng)問(wèn)此資源的線(xiàn)程都有機(jī)會(huì)得到此互斥對(duì)象。

              以互斥內(nèi)核對(duì)象來(lái)保持線(xiàn)程同步可能用到的函數(shù)主要有CreateMutex()、OpenMutex()、ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥對(duì)象前,首先要通過(guò)CreateMutex()或OpenMutex()創(chuàng)建或打開(kāi)一個(gè)互斥對(duì)象。CreateMutex()函數(shù)原型為:

            HANDLE CreateMutex(
             LPSECURITY_ATTRIBUTES lpMutexAttributes, 
            // 安全屬性指針
             BOOL bInitialOwner, // 初始擁有者
             LPCTSTR lpName // 互斥對(duì)象名
            );


              參數(shù)bInitialOwner主要用來(lái)控制互斥對(duì)象的初始狀態(tài)。一般多將其設(shè)置為FALSE,以表明互斥對(duì)象在創(chuàng)建時(shí)并沒(méi)有為任何線(xiàn)程所占有。如果在創(chuàng)建互斥對(duì)象時(shí)指定了對(duì)象名,那么可以在本進(jìn)程其他地方或是在其他進(jìn)程通過(guò)OpenMutex()函數(shù)得到此互斥對(duì)象的句柄。OpenMutex()函數(shù)原型為:

            HANDLE OpenMutex(
             DWORD dwDesiredAccess, 
            // 訪(fǎng)問(wèn)標(biāo)志
             BOOL bInheritHandle, // 繼承標(biāo)志
             LPCTSTR lpName // 互斥對(duì)象名
            );


              當(dāng)目前對(duì)資源具有訪(fǎng)問(wèn)權(quán)的線(xiàn)程不再需要訪(fǎng)問(wèn)此資源而要離開(kāi)時(shí),必須通過(guò)ReleaseMutex()函數(shù)來(lái)釋放其擁有的互斥對(duì)象,其函數(shù)原型為:

            BOOL ReleaseMutex(HANDLE hMutex);


              其唯一的參數(shù)hMutex為待釋放的互斥對(duì)象句柄。至于WaitForSingleObject()和WaitForMultipleObjects()等待函數(shù)在互斥對(duì)象保持線(xiàn)程同步中所起的作用與在其他內(nèi)核對(duì)象中的作用是基本一致的,也是等待互斥內(nèi)核對(duì)象的通知。但是這里需要特別指出的是:在互斥對(duì)象通知引起調(diào)用等待函數(shù)返回時(shí),等待函數(shù)的返回值不再是通常的WAIT_OBJECT_0(對(duì)于WaitForSingleObject()函數(shù))或是在WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之間的一個(gè)值(對(duì)于WaitForMultipleObjects()函數(shù)),而是將返回一個(gè)WAIT_ABANDONED_0(對(duì)于WaitForSingleObject()函數(shù))或是在WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1之間的一個(gè)值(對(duì)于WaitForMultipleObjects()函數(shù))。以此來(lái)表明線(xiàn)程正在等待的互斥對(duì)象由另外一個(gè)線(xiàn)程所擁有,而此線(xiàn)程卻在使用完共享資源前就已經(jīng)終止。除此之外,使用互斥對(duì)象的方法在等待線(xiàn)程的可調(diào)度性上同使用其他幾種內(nèi)核對(duì)象的方法也有所不同,其他內(nèi)核對(duì)象在沒(méi)有得到通知時(shí),受調(diào)用等待函數(shù)的作用,線(xiàn)程將會(huì)掛起,同時(shí)失去可調(diào)度性,而使用互斥的方法卻可以在等待的同時(shí)仍具有可調(diào)度性,這也正是互斥對(duì)象所能完成的非常規(guī)操作之一。

              在編寫(xiě)程序時(shí),互斥對(duì)象多用在對(duì)那些為多個(gè)線(xiàn)程所訪(fǎng)問(wèn)的內(nèi)存塊的保護(hù)上,可以確保任何線(xiàn)程在處理此內(nèi)存塊時(shí)都對(duì)其擁有可靠的獨(dú)占訪(fǎng)問(wèn)權(quán)。下面給出的示例代碼即通過(guò)互斥內(nèi)核對(duì)象hMutex對(duì)共享內(nèi)存快g_cArray[]進(jìn)行線(xiàn)程的獨(dú)占訪(fǎng)問(wèn)保護(hù)。下面給出實(shí)現(xiàn)代碼清單:

            // 互斥對(duì)象
            HANDLE hMutex = NULL;
            char g_cArray[10];
            UINT ThreadProc18(LPVOID pParam)
            {
             
            // 等待互斥對(duì)象通知
             WaitForSingleObject(hMutex, INFINITE);
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[i] 
            = 'a';
              Sleep(
            1);
             }

             
            // 釋放互斥對(duì)象
             ReleaseMutex(hMutex);
             
            return 0;
            }

            UINT ThreadProc19(LPVOID pParam)
            {
             
            // 等待互斥對(duì)象通知
             WaitForSingleObject(hMutex, INFINITE);
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[
            10 - i - 1= 'b';
              Sleep(
            1);
             }

             
            // 釋放互斥對(duì)象
             ReleaseMutex(hMutex);
             
            return 0;
            }

            ……
            void CSample08View::OnMutex() 
            {
             
            // 創(chuàng)建互斥對(duì)象
             hMutex = CreateMutex(NULL, FALSE, NULL);
             
            // 啟動(dòng)線(xiàn)程
             AfxBeginThread(ThreadProc18, NULL);
             AfxBeginThread(ThreadProc19, NULL);
             
            // 等待計(jì)算完畢
             Sleep(300);
             
            // 報(bào)告計(jì)算結(jié)果
             CString sResult = CString(g_cArray);
             AfxMessageBox(sResult);
            }


              互斥對(duì)象在MFC中通過(guò)CMutex類(lèi)進(jìn)行表述。使用CMutex類(lèi)的方法非常簡(jiǎn)單,在構(gòu)造CMutex類(lèi)對(duì)象的同時(shí)可以指明待查詢(xún)的互斥對(duì)象的名字,在構(gòu)造函數(shù)返回后即可訪(fǎng)問(wèn)此互斥變量。CMutex類(lèi)也是只含有構(gòu)造函數(shù)這唯一的成員函數(shù),當(dāng)完成對(duì)互斥對(duì)象保護(hù)資源的訪(fǎng)問(wèn)后,可通過(guò)調(diào)用從父類(lèi)CSyncObject繼承的UnLock()函數(shù)完成對(duì)互斥對(duì)象的釋放。CMutex類(lèi)構(gòu)造函數(shù)原型為:

            CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );


              該類(lèi)的適用范圍和實(shí)現(xiàn)原理與API方式創(chuàng)建的互斥內(nèi)核對(duì)象是完全類(lèi)似的,但要簡(jiǎn)潔的多,下面給出就是對(duì)前面的示例代碼經(jīng)CMutex類(lèi)改寫(xiě)后的程序?qū)崿F(xiàn)清單:

            // MFC互斥類(lèi)對(duì)象
            CMutex g_clsMutex(FALSE, NULL);
            UINT ThreadProc27(LPVOID pParam)
            {
             
            // 等待互斥對(duì)象通知
             g_clsMutex.Lock();
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[i] 
            = 'a';
              Sleep(
            1);
             }

             
            // 釋放互斥對(duì)象
             g_clsMutex.Unlock();
             
            return 0;
            }

            UINT ThreadProc28(LPVOID pParam)
            {
             
            // 等待互斥對(duì)象通知
             g_clsMutex.Lock();
             
            // 對(duì)共享資源進(jìn)行寫(xiě)入操作
             for (int i = 0; i < 10; i++)
             
            {
              g_cArray[
            10 - i - 1= 'b';
              Sleep(
            1);
             }

             
            // 釋放互斥對(duì)象
             g_clsMutex.Unlock();
             
            return 0;
            }

            ……
            void CSample08View::OnMutexMfc() 
            {
             
            // 啟動(dòng)線(xiàn)程
             AfxBeginThread(ThreadProc27, NULL);
             AfxBeginThread(ThreadProc28, NULL);
             
            // 等待計(jì)算完畢
             Sleep(300);
             
            // 報(bào)告計(jì)算結(jié)果
             CString sResult = CString(g_cArray);
             AfxMessageBox(sResult);
            }


              小結(jié)

              線(xiàn)程的使用使程序處理更夠更加靈活,而這種靈活同樣也會(huì)帶來(lái)各種不確定性的可能。尤其是在多個(gè)線(xiàn)程對(duì)同一公共變量進(jìn)行訪(fǎng)問(wèn)時(shí)。雖然未使用線(xiàn)程同步的程序代碼在邏輯上或許沒(méi)有什么問(wèn)題,但為了確保程序的正確、可靠運(yùn)行,必須在適當(dāng)?shù)膱?chǎng)合采取線(xiàn)程同步措施。

            posted on 2008-01-06 14:41 譚文政 閱讀(540) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): C/C++
            狠狠色婷婷久久一区二区| 国产精品亚洲综合久久| 久久久久亚洲av无码专区喷水| 亚洲伊人久久成综合人影院| 久久综合久久美利坚合众国| 久久人人青草97香蕉| 777午夜精品久久av蜜臀| 久久精品中文无码资源站| 久久婷婷五月综合国产尤物app| 久久影院综合精品| 久久国产精品久久久| 久久AⅤ人妻少妇嫩草影院| 国产成人精品久久一区二区三区| 国产精品久久久久久福利漫画| 91精品国产综合久久四虎久久无码一级| 88久久精品无码一区二区毛片 | 99久久国产宗和精品1上映| 91精品国产91久久综合| 国产亚洲美女精品久久久| 亚洲精品美女久久777777| 国产精品久久久久久久午夜片| 久久99精品久久久大学生| 久久99热这里只有精品国产| 99久久久精品免费观看国产| 亚洲精品国产第一综合99久久| 777久久精品一区二区三区无码| 午夜精品久久久久久久| 国产精品亚洲综合久久| 久久影院午夜理论片无码| av无码久久久久久不卡网站| 久久精品国产免费观看| 亚洲精品午夜国产VA久久成人| 久久美女人爽女人爽| 色综合久久综合中文综合网 | 久久精品国产精品青草app| 欧美日韩久久中文字幕| 午夜精品久久久内射近拍高清| 国产 亚洲 欧美 另类 久久| 99久久精品国内| 99久久免费国产精精品| 久久精品国产亚洲AV香蕉|