• <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>
            線程同步是多線程程序設(shè)計的核心內(nèi)容,它的目的是正確處理多線程并發(fā)時的各種問題,例如線程的等待、多個線程訪問同一數(shù)據(jù)時的互斥,防死鎖等。Win32提供多種內(nèi)核對象和手段用于線程同步,如互斥量、信號量、事件、臨界區(qū)等。所不同的是,互斥量、信號量、事件都是Windows的內(nèi)核對象,當程序?qū)@些對象進行控制時會自動轉(zhuǎn)換到核心態(tài),而臨界區(qū)本身不是內(nèi)核對象,它是工作在用戶態(tài)的。我們知道從用戶態(tài)轉(zhuǎn)換到核心態(tài)是需要以時間為代價的,所以如果能在用戶態(tài)就簡單解決的問題,就可以不必勞煩核心態(tài)了。
            這里我要說的是兩種用于C++的多線程同步類,通過對這兩種類的使用就可以方便的實現(xiàn)對變量或代碼段的加鎖控制,從而防止多線程對變量不正確的操作。
            所謂加鎖,就是說當我們要訪問某關(guān)鍵變量之前,都需要首先獲得允許才能繼續(xù),如果未獲得允許則只有等待。一個關(guān)鍵變量擁有一把鎖,一個線程必須先得到這把鎖(其實稱為鑰匙可能更形象)才可以訪問這個變量,而當某個變量持有這把鎖的時候,其他線程就不能重復的得到它,只有等持有鎖的線程把鎖歸還以后其他線程才有可能得到它。之所以這樣做,就是為了防止一個線程讀取某對象途中另一線程對它進行了修改,或兩線程同時對一變量進行修改,例如:
            // 全局:
            ??????? struct ?MyStruct? {? int ?a,?b;?} ;
            ???????MyStruct?s;
            // 線程1:

            ??????? int ?a? = ?s.a;
            ???????
            int ?b? =
            ?s.b;
            // 線程2:

            ???????s.a ++ ;
            ???????s.b
            -- ;
            如果實際的執(zhí)行順序就是上述書寫的順序那到?jīng)]有什么,但如果線程2的執(zhí)行打斷了線程1,變?yōu)槿缦马樞颍?/font>
            ?????? int a = s.a;????? //線程1
            ?????? s.a++;??????????? //線程2
            ?????? s.b++;??????????? //線程2
            ?????? int b = s.b;????? //線程1
            那么這時線程1讀出來的a和b就會有問題了,因為a是在修改前讀的,而b是在修改后讀的,這樣讀出來的是不完整的數(shù)據(jù),會對程序帶來不可預料的后果。天知道兩個程的調(diào)度順序是什么樣的。為了防止這種情況的出現(xiàn),需要對變量s加鎖,也就是當線程1得到鎖以后就可以放心的訪問s,這時如果線程2要修改s,只有等線程1訪問完成以后將鎖釋放才可以,從而保證了上述兩線程交叉訪問變量的情況不會出現(xiàn)。
            使用Win32提供的臨界區(qū)可以方便的實現(xiàn)這種鎖:
            // 全局:
            ???????CRITICAL_SECTION?cs;
            ???????InitializeCriticalSection(
            &
            cs);
            // 線程1:

            ???????EnterCriticalSection( & cs);
            ???????
            int ?a? =
            ?s.a;
            ???????
            int ?b? =
            ?s.b;
            ???????LeaveCriticalSection(
            &
            cs);
            // 線程2:

            ???????EnterCriticalSection( & cs);
            ???????s.a
            ++
            ;
            ???????s.b
            --
            ;
            ???????LeaveCriticalSection(
            &
            cs);
            // 最后:

            ???????DeleteCriticalSection( & cs);
            代碼中的臨界區(qū)變量(cs)就可以看作是變量s的鎖,當函數(shù)EnterCriticalSection返回時,當前線程就獲得了這把鎖,之后就是對變量的訪問了。訪問完成后,調(diào)用LeaveCriticalSection表示釋放這把鎖,允許其他線程繼續(xù)使用它。
            如果每當需要對一個變量進行加鎖時都需要做這些操作,顯得有些麻煩,而且變量cs與s只有邏輯上的鎖關(guān)系,在語法上沒有什么聯(lián)系,這對于鎖的管理帶來了不小的麻煩。程序員總是最懶的,可以想出各種偷懶的辦法來解決問題,例如讓被鎖的變量與加鎖的變量形成物理上的聯(lián)系,使得鎖變量成為被鎖變量不可分割的一部分,這聽起來是個好主意。
            首先想到的是把鎖封閉在一個類里,讓類的構(gòu)造函數(shù)和析構(gòu)函數(shù)來管理對鎖的初始化和鎖毀動作,我們稱這個鎖為“實例鎖”:
            ??????? class ?InstanceLockBase
            ???????
            {
            ??????????????CRITICAL_SECTION?cs;
            ???????
            protected
            :
            ??????????????InstanceLockBase()?
            {?InitialCriticalSection( & cs);?}

            ??????????????
            ~ InstanceLockBase()? {?DeleteCriticalSection( & cs);?}
            ???????}
            ;
            如果熟悉C++,看到這里一定知道后面我要干什么了,對了,就是繼承,因為我把構(gòu)造函數(shù)和析構(gòu)函數(shù)都聲明為保護的(protected),這樣唯一的作用就是在子類里使用它。讓我們的被保護數(shù)據(jù)從這個類繼承,那么它們不就不可分割了嗎:
            ??????? struct ?MyStruct:? public ?InstanceLockBase
            ???????
            {?…?} ;
            什么?結(jié)構(gòu)體還能從類繼承?當然,C++中結(jié)構(gòu)體和類除了成員的默認訪問控制不同外沒有什么不一樣,class能做的struct也能做。此外,也許你還會問,如果被鎖的是個簡單類型,不能繼承怎么辦,那么要么用一個類對這個簡單類型進行封裝(記得Java里有int和Integer嗎),要么只好手工管理它們的聯(lián)系了。如果被鎖類已經(jīng)有了基類呢?沒關(guān)系,C++是允許多繼承的,多一個基類也沒什么。
            現(xiàn)在我們的數(shù)據(jù)里面已經(jīng)包含一把鎖了,之后就是要添加加鎖和解鎖的動作,把它們作為InstanceLockBase類的成員函數(shù)再合適不過了:
            ??????? class ?InstanceLockBase
            ???????
            {
            ??????????????CRITICAL_SECTION?cs;
            ??????????????
            void ?Lock()? {?EnterCriticalSection( & cs);?}

            ??????????????
            void ?Unlock()? {?LeaveCriticalSection( & cs);?}
            ??????????????…
            }
            ;
            看到這里可能會發(fā)現(xiàn),我把Lock和Unlock函數(shù)都聲明為私有了,那么如何訪問這兩個函數(shù)呢?是的,我們總是需要有一個地方來調(diào)用這兩個函數(shù)以實現(xiàn)加鎖和解鎖的,而且它們總應(yīng)該成對出現(xiàn),但C++語法本身沒能限制我們必須成對的調(diào)用兩個函數(shù),如果加完鎖忘了解,那后果是嚴重的。這里有一個例外,就是C++對于構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用是自動成對的,對了,那就把對Lock和Unlock的調(diào)用專門寫在一個類的構(gòu)造函數(shù)和析構(gòu)函數(shù)中:
            class ?InstanceLock
            {
            ??????????????InstanceLockBase
            *
            ?_pObj;
            public
            :
            ??????????????InstanceLock(InstanceLockBase
            *
            ?pObj)
            ??????????????
            {
            ?????????????????????_pObj?
            = ?pObj;? // 這里會保存一份指向s的指針,用于解鎖

            ????????????????????? if (NULL? != ?_pObj)
            ????????????????????????????_pObj
            -> Lock();?????? // 這里加鎖

            ??????????????}

            ??????????????
            ~ InstanceLock()
            ??????????????
            {
            ?????????????????????
            if (NULL? !=
            ?_pObj)
            ????????????????????????????_pObj
            -> Unlock();??? // 這里解鎖

            ??????????????}

            }
            ;
            最后別忘了在類InstanceLockBase中把InstanceLock聲明為友元,使得它能正確訪問Lock和Unlock這兩個私有函數(shù):
            class ?InstanceLockBase
            {
            ??????????????friend?
            class
            ?InstanceLock;
            ??????????????…
            }
            ;
            好了,有了上面的基礎(chǔ),現(xiàn)在對變量s的加解鎖管理變成了對InstanceLock的實例的生命周期的管理了。假如我們有一個函數(shù)ModifyS中要對s進行修改,那么只要在函數(shù)一開始就聲明一個InstaceLock的實例,這樣整個函數(shù)就自動對s加鎖,一旦進入這個函數(shù),其他線程就都不能獲得s的鎖了:
            ??????? void ?ModifyS()
            ???????
            {
            ??????????????InstanceLock?
            lock ( & s);???????? //
            這里已經(jīng)實現(xiàn)加鎖了
            ??????????????
            // some?operations?on?s

            ???????}
            ????
            ?????????????????????????????????????????? //
            一旦離開lock對象的作用域,自動解鎖
            如果是要對某函數(shù)中一部分代碼加鎖,只要用一對大括號把它們括起來再聲明一個lock就可以了:
            ???????…
            ???????
            {
            ??????????????InstanceLock?
            lock ( &
            s);
            ??????????????
            // ?do?something?…

            ???????}

            ???????…
            好了,就是這么簡單。下面來看一個測試。
            首先準備一個輸出函數(shù),對我們理解程序有幫助。它會在輸出我們想輸出的內(nèi)容同時打出行號和時間:
            void ?Say( char * ?text)
            {
            ??????????????
            static ? int ?count? = ? 0
            ;
            ??????????????SYSTEMTIME?st;
            ??????????????::GetLocalTime(
            &
            st);
            ??????????????printf(
            " %03d?[%02d:%02d:%02d.%03d]%s " ,? ++
            count,?st.wHour,?st.wMinute,?st.wSecond,?st.wMilliseconds,?text);
            }
            當然,原則上當多線程都調(diào)用這個函數(shù)時應(yīng)該對其靜態(tài)局部變量count進行加鎖,這里就省略了。
            我們聲明一個非常簡單的被鎖的類型,并生成一個實例:
            class ?MyClass:? public ?InstanceLockBase
            {}
            ;
            MyClass?mc;
            子線程的任務(wù)就是對這個對象加鎖,然后輸出一些信息:
            DWORD?CALLBACK?ThreadProc(LPVOID?param)
            {
            ??????????????InstanceLock?il(
            &
            mc);
            ??????????????Say(
            " in?sub?thread,?lock "
            );
            ??????????????Sleep(
            2000
            );
            ??????????????Say(
            " in?sub?thread,?unlock "
            );
            ??????????????
            return ? 0
            ;
            }
            這里會輸出兩條信息,一是在剛剛獲得鎖的時間,二是在釋放鎖的時候,中間通過Sleep來延遲2秒。
            主線程負責開啟子線程,然后也對mc加鎖:
            CreateThread( 0 ,? 0 ,?ThreadProc,? 0 ,? 0 ,? 0 );
            ???????
            {
            ??????????????InstanceLock?il(
            &
            mc);
            ??????????????Say(
            " in?main?thread,?lock "
            );
            ??????????????Sleep(
            3000
            );
            ??????????????Say(
            " in?main?thread,?lock "
            );
            ???????}
            運行此程序,得到的輸出如下:
            001 [13:43:23.781]in main thread, lock
            002 [13:43:26.781]in main thread, lock
            003 [13:43:26.781]in sub thread, lock
            004 [13:43:28.781]in sub thread, unlock
            從其輸出的行號和時間可以清楚的看到兩個線程間的互斥:當主線程恰好首先獲得鎖時,它會延遲3秒,然后釋放鎖,之后子線程才得以繼續(xù)進行。這個例子也證明我們的類工作的很好。
            總結(jié)一下,要使用InstanceLock系列類,要做的就是:
            1 讓被鎖類從InstanceLockBase繼承
            2 所有要訪問被鎖對象的代碼前面聲明InstanceLock的實例,并傳入被鎖對象的指針。
            ?
            附:完整源代碼:
            #pragma ?once
            #include?
            < windows.h >
            ?
            class ?InstanceLock;
            ?
            class
            ?InstanceLockBase
            {
            ???????friend?
            class
            ?InstanceLock;
            ?
            ???????CRITICAL_SECTION?cs;
            ?
            ???????
            void
            ?Lock()
            ???????
            {
            ??????????????::EnterCriticalSection(
            &
            cs);
            ???????}

            ?
            ???????
            void ?Unlock()
            ???????
            {
            ??????????????::LeaveCriticalSection(
            &
            cs);
            ???????}

            ?
            protected :
            ???????InstanceLockBase()
            ???????
            {
            ??????????????::InitializeCriticalSection(
            &
            cs);
            ???????}

            ?
            ???????
            ~ InstanceLockBase()
            ???????
            {
            ??????????????::DeleteCriticalSection(
            &
            cs);
            ???????}

            }
            ;
            ?
            class
            ?InstanceLock
            {
            ???????InstanceLockBase
            *
            ?_pObj;
            public
            :
            ???????InstanceLock(InstanceLockBase
            *
            ?pObj)
            ???????
            {
            ??????????????_pObj?
            =
            ?pObj;
            ??????????????
            if (NULL? !=
            ?_pObj)
            ?????????????????????_pObj
            ->
            Lock();
            ???????}

            ?
            ???????
            ~ InstanceLock()
            ???????
            {
            ??????????????
            if (NULL? !=
            ?_pObj)
            ?????????????????????_pObj
            ->
            Unlock();
            ???????}

            }
            ;
            Posted on 2006-11-06 10:59 艾凡赫 閱讀(364) 評論(0)  編輯 收藏 引用 所屬分類: C++
            亚洲国产精品久久久久婷婷老年 | 久久精品国产久精国产果冻传媒| 久久午夜无码鲁丝片| 久久人妻AV中文字幕| 婷婷久久综合| 久久影院亚洲一区| 久久久久九九精品影院| 久久国产精品免费一区| 国产激情久久久久影院老熟女| 久久伊人精品青青草原高清| 97久久超碰国产精品2021| 久久久久人妻一区精品性色av| 性高湖久久久久久久久| 久久综合狠狠综合久久综合88| 伊人久久大香线蕉av不卡 | 亚洲精品99久久久久中文字幕 | 亚洲综合久久夜AV | 久久亚洲AV无码精品色午夜| 午夜精品久久久久| 精品人妻伦九区久久AAA片69| 精品熟女少妇AV免费久久 | 人妻少妇久久中文字幕一区二区| 国产99久久久国产精品小说| 久久无码高潮喷水| 久久99国产精品尤物| 久久精品成人免费看| 久久精品国产亚洲av瑜伽| 久久久精品国产| 久久精品水蜜桃av综合天堂| 97久久久精品综合88久久| 国产精品美女久久久久av爽| 性做久久久久久久久| 漂亮人妻被黑人久久精品| 青青热久久综合网伊人| 亚洲午夜精品久久久久久app| 亚洲色欲久久久综合网东京热| 69久久精品无码一区二区| 久久久久女教师免费一区| 热re99久久6国产精品免费| 久久国产精品免费一区| 亚洲女久久久噜噜噜熟女|