• <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>

            每天早晨叫醒你的不是鬧鐘,而是夢(mèng)想

              C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
              62 Posts :: 0 Stories :: 5 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(1)

            我參與的團(tuán)隊(duì)

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            http://www.c-view.org/journal/006/gp_aa.htm 


            并不是我故意想弄糟你的心情,但是在這期專欄里,我們將討論多線程編程這一話題。正如上一期Generic里所說(shuō)的,編寫異常安全(exception-safe)的程序是非常困難的,但是和編寫多線程程序比起來(lái),那簡(jiǎn)直就是兒戲。  

            多線程的程序是出了名的難編寫、難驗(yàn)證、難調(diào)試、難維護(hù),這通常是件苦差事。不正確的多線程程序可能可以運(yùn)行很多年也不出一點(diǎn)錯(cuò),直到滿足某些臨界的條件時(shí),才出現(xiàn)意想不到的奇怪錯(cuò)誤。  

            不用說(shuō),編寫多線程程序的程序員需要使用可能得到的所有幫助。這期專欄將專注于討論競(jìng)爭(zhēng)條件(race conditions)——這通常是多線程程序中各種麻煩的根源——深入了解它并提供一些工具來(lái)防止競(jìng)爭(zhēng)。令人驚異的是,我們將讓編譯器盡其所能來(lái)幫助你做這些事。

            僅僅一個(gè)不起眼的關(guān)鍵字 
            盡管C和C++標(biāo)準(zhǔn)對(duì)于線程都明顯的“保持沉默”,但它們以volatile關(guān)鍵字的形式,確實(shí)為多線程保留了一點(diǎn)特權(quán)。  

            就象大家更熟悉的const一樣,volatile是一個(gè)類型修飾符(type modifier)。它是被設(shè)計(jì)用來(lái)修飾被不同線程訪問和修改的變量。如果沒有volatile,基本上會(huì)導(dǎo)致這樣的結(jié)果:要么無(wú)法編寫多線程程序,要么編譯器失去大量?jī)?yōu)化的機(jī)會(huì)。下面我們來(lái)一個(gè)個(gè)說(shuō)明。  

            考慮下面的代碼:  

             1class Gadget
             2{
             3public:
             4    void Wait()
             5    {
             6        while (!flag_)
             7        {
             8            Sleep(1000); // sleeps for 1000 milliseconds
             9        }

            10    }

            11    void Wakeup()
            12    
            13        flag_ = true
            14    }

            15    
            16private
            17    bool flag_; 
            18}
            ;
            19
            20


             
            上面代碼中Gadget::Wait的目的是每過(guò)一秒鐘去檢查一下flag_成員變量,當(dāng)flag_被另一個(gè)線程設(shè)為true時(shí),該函數(shù)才會(huì)返回。至少這是程序作
            者的意圖,然而,這個(gè)Wait函數(shù)是錯(cuò)誤的.假設(shè)編譯器發(fā)現(xiàn)Sleep(1000)是調(diào)用一個(gè)外部的庫(kù)函數(shù),它不會(huì)改變成員變量flag_,那么編譯器就可
            以斷定它可以把flag_緩存在寄存器中,以后可以訪問該寄存器來(lái)代替訪問較慢的主板上的內(nèi)存。這對(duì)于單線程代碼來(lái)說(shuō)是一個(gè)很好的優(yōu)化,但
            是在現(xiàn)在這種情況下,它破壞了程序的正確性:當(dāng)你調(diào)用了某個(gè)Gadget的Wait函數(shù)后,即使另一個(gè)線程調(diào)用了Wakeup,Wait還是會(huì)一直循環(huán)下去。
            這是因?yàn)閒lag_的改變沒有反映到緩存它的寄存器中去。編譯器的優(yōu)化未免有點(diǎn)太……樂觀了。

            在大多數(shù)情況下,把變量緩存在寄存器中是一個(gè)非常有價(jià)值的優(yōu)化方法,如果不用的話很可惜。C和C++給你提供了顯式禁用這種緩存優(yōu)化的機(jī)會(huì)。
            如果你聲明變量是使用了volatile修飾符,編譯器就不會(huì)把這個(gè)變量緩存在寄存器里——每次訪問都將去存取變量在內(nèi)存中的實(shí)際位置。這樣你
            要對(duì)Gadget的Wait/Wakeup做的修改就是給flag_加上正確的修飾:  

            class Gadget 
            { 
            public: 
                ... as above ... 
            private: 
                volatile bool flag_; 
            }; 



            大多數(shù)關(guān)于volatile的原理和用法的解釋就到此為止,并且建議你用volatile修飾在多個(gè)線程中使用的原生類型變量。然而,你可以用volatile
            做更多的事,因?yàn)樗巧衿娴腃++類型系統(tǒng)的一部分。




            把volatile用于自定義類型 
            volatile修飾不僅可以用于原生類型,也可以用于自定義類型。這時(shí)候,volatile修飾方式類似于const(你也可以對(duì)一個(gè)類型同時(shí)使用const
            和volatile).

            與const不同,volatile的作用對(duì)于原生類型和自定義類型是有區(qū)別的。就是說(shuō),原生類型有volatile修飾時(shí),仍然支持它們的各種操作(加、
            乘、賦值等等),然而對(duì)于class來(lái)說(shuō),就不是這樣。舉例來(lái)說(shuō),你可以把一個(gè)非volatile的int的值賦給一個(gè)volatile的int,但是你不能把一
            個(gè)非volatile的對(duì)象賦給一個(gè)volatile對(duì)象。 

            讓我們舉個(gè)例子來(lái)說(shuō)明自定義類型的volatile是怎么工作的。  
            class Gadget

            {
            public:
                void Foo() volatile;
                void Bar();
                ...
            private:
                String name_;
                int state_;
            };
            ...
            
            Gadget regularGadget;
            volatile Gadget volatileGadget;



            如果你認(rèn)為volatile對(duì)于對(duì)象來(lái)說(shuō)沒有什么作用的話,那你可要大吃一驚了。  
            volatileGadget.Foo(); // ok, volatile fun called for 
                                      // volatile object 
            regularGadget.Foo();  // ok, volatile fun called for 
                                      // non-volatile object 
            volatileGadget.Bar(); // error! Non-volatile function called for 
                                      // volatile object! 

            從沒有volatile修飾的類型到相應(yīng)的volatile類型的轉(zhuǎn)換是很平常的。但是,就象const一樣,你不能反過(guò)來(lái)把volatile類型轉(zhuǎn)換為非volatile類型。你必須用類型轉(zhuǎn)換運(yùn)算符:  
            Gadget& ref = const_cast<Gadget&>;(volatileGadget); 
            ref.Bar(); // ok 

            一個(gè)有volatile修飾的類只允許訪問其接口的一個(gè)子集,這個(gè)子集由類的實(shí)現(xiàn)者來(lái)控制。用戶只有用const_cast才可以訪問這個(gè)類型的全部接口。而且,象const一樣,類的volatile屬性會(huì)傳遞給它的成員(例如,volatileGadget.name_和volatileGadget.state_也是volatile變量)。

            volatile,臨界區(qū)和競(jìng)爭(zhēng)條件 
            多線程程序中最簡(jiǎn)單也是最常用的同步機(jī)制要算是mutex(互斥對(duì)象)了。一個(gè)mutex只提供兩個(gè)基本操作:Acquire和Release。一旦某個(gè)線程調(diào)用了Acquire,其他線程再調(diào)用Acquire時(shí)就會(huì)被阻塞。當(dāng)這個(gè)線程調(diào)用Release后,剛才阻塞在Acquire里的線程中,會(huì)有一個(gè)且僅有一個(gè)被喚醒。換句話說(shuō),對(duì)于一個(gè)給定的mutex,只有一個(gè)線程可以在Acquire和Release調(diào)用之間獲取處理器時(shí)間。在Acquire和Release調(diào)用之間執(zhí)行的代碼叫做臨界區(qū)(critical section)。(Windows的用語(yǔ)可能會(huì)引起一點(diǎn)混亂,因?yàn)閃indows把mutex本身叫做臨界區(qū),而Windows的mutex實(shí)際上指進(jìn)程間的mutex。如果把它們分別叫作線程mutex和進(jìn)程mutex可能會(huì)好些。)  

            Mutex是用來(lái)避免數(shù)據(jù)出現(xiàn)競(jìng)爭(zhēng)條件。根據(jù)定義,所謂競(jìng)爭(zhēng)條件就是這樣一種情況:多個(gè)線程對(duì)數(shù)據(jù)產(chǎn)生的作用要依賴于線程的調(diào)度順序的。當(dāng)兩個(gè)線程競(jìng)相訪問同一數(shù)據(jù)時(shí),就會(huì)發(fā)生競(jìng)爭(zhēng)條件。因?yàn)橐粋€(gè)線程可以在任意一個(gè)時(shí)刻打斷其他線程,數(shù)據(jù)可能會(huì)被破壞或者被錯(cuò)誤地解釋。因此,對(duì)數(shù)據(jù)的修改操作,以及有些情況下的訪問操作,必須用臨界區(qū)保護(hù)起來(lái)。在面向?qū)ο蟮木幊讨校@通常意味著你在一個(gè)類的成員變量中保存一個(gè)mutex,然后在你訪問這個(gè)類的狀態(tài)時(shí)使用這個(gè)mutex。  

            多線程編程高手看了上面兩個(gè)段落,可能已經(jīng)在打哈欠了,但是它們的目的只是提供一個(gè)準(zhǔn)備練習(xí),我們現(xiàn)在要和volatile聯(lián)系起來(lái)了。我們將把C++的類型和線程的語(yǔ)義作一個(gè)對(duì)比。

            在一個(gè)臨界區(qū)以外,任意線程會(huì)在任何時(shí)間打斷別的線程;這是不受控制的,所以被多個(gè)線程訪問的變量容易被改得面目全非。這和volatile的原意[1]是一致的——所以需要用volatile來(lái)防止編譯器無(wú)意地緩存這樣的變量。  

            在由一個(gè)mutex限定的臨界區(qū)里,只有一個(gè)線程可以進(jìn)入。因此,在臨界區(qū)中執(zhí)行的代碼有和單線程程序有相同的語(yǔ)義。被控制的變量不會(huì)再被意外改變——你可以去掉volatile修飾。  

            簡(jiǎn)而言之,線程間共享的數(shù)據(jù)在臨界區(qū)之外是volatile的,而在臨界區(qū)之內(nèi)則不是。  

            你通過(guò)對(duì)一個(gè)mutex加鎖來(lái)進(jìn)入一個(gè)臨界區(qū),然后你用const_cast去掉某個(gè)類型的volatile修飾,如果我們能成功地把這兩個(gè)操作放到一起,那么我們就在C++類型系統(tǒng)和應(yīng)用程序的線程語(yǔ)義建立起聯(lián)系。這樣我們可以讓編譯器來(lái)幫我們檢測(cè)競(jìng)爭(zhēng)條件。

            LockingPtr 
            我們需要有一個(gè)工具來(lái)做mutex的獲取和const_cast兩個(gè)操作。讓我們來(lái)設(shè)計(jì)一個(gè)LockingPtr類,你需要用一個(gè)volatile的對(duì)象obj和一個(gè)mutex對(duì)象mtx來(lái)初始化它。在LockingPtr對(duì)象的生命期中,它會(huì)保證mtx處于被獲取狀態(tài),而且也提供對(duì)去掉volatile修飾的obj的訪問。對(duì)obj的訪問類似于smart pointer,是通過(guò)operator->;和operator*來(lái)進(jìn)行的。const_cast是在LockingPtr內(nèi)部進(jìn)行。這個(gè)轉(zhuǎn)化在語(yǔ)義上是正確的,因?yàn)長(zhǎng)ockingPtr在其生存期中始終擁有mutex。  

            首先,我們來(lái)定義和LockingPtr一起工作的Mutex類的框架:  

            class Mutex

            {

            public:

                void Acquire();

                void Release();

                ...    

            };


            為了使用LockingPtr,你需要用操作系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu)和底層函數(shù)來(lái)實(shí)現(xiàn)Mutex。  
            LockingPtr是一個(gè)模板,用被控制變量的類型作為模板參數(shù)。例如,如果你希望控制一個(gè)Widget,你就要這樣寫LockingPtr <Widget>;。  

            LockingPtr的定義很簡(jiǎn)單,它只是實(shí)現(xiàn)了一個(gè)單純的smart pointer。它關(guān)注的焦點(diǎn)只是在于把const_cast和臨界區(qū)操作放在一起。  

            template <typename T>;

            class LockingPtr 
            {

            public:

               // Constructors/destructors

               LockingPtr(volatile T& obj, Mutex& mtx)

                   : pObj_(const_cast<T*>;(&obj)),

                    pMtx_(&mtx)

               {    mtx.Lock();    }

               ~LockingPtr()

               {    pMtx_->;Unlock();    }

               // Pointer behavior

               T& operator*()

               {    return *pObj_;    }

               T* operator->;()

               {   return pObj_;   }

            private:
               T* pObj_;

               Mutex* pMtx_;

               LockingPtr(const LockingPtr&);

               LockingPtr& operator=(const LockingPtr&);

            };

            盡管很簡(jiǎn)單,LockingPtr對(duì)于編寫正確的多線程代碼非常有用。你應(yīng)該把線程間共享的對(duì)象聲明為volatile,但是永遠(yuǎn)不要對(duì)它們使用
            const_cast——你應(yīng)該始終是用LockingPtr的自動(dòng)對(duì)象(automatic objects)。讓我們舉例來(lái)說(shuō)明。

            比如說(shuō)你有兩個(gè)線程需要共享一個(gè)vector<char>;對(duì)象: 

            class SyncBuf {
            
            public:
                void Thread1();
                void Thread2();
            private:
                typedef vector<char>; BufT;
                volatile BufT buffer_;
                Mutex mtx_; // controls access to buffer_
            };

            在一個(gè)線程的函數(shù)里,你只需要簡(jiǎn)單地使用一個(gè)LockingPtr<BufT>;對(duì)象來(lái)獲取對(duì)buffer_成員變量的受控訪問:

            void SyncBuf::Thread1() {
            
                LockingPtr<BufT>; lpBuf(buffer_, mtx_);
                BufT::iterator i = lpBuf->;begin();
                for (; i != lpBuf->;end(); ++i) {
                    ... use *i ...
                }
            }

            這樣的代碼很容易編寫,也很容易理解——每當(dāng)你需要使用buffer_時(shí),你必須創(chuàng)建一個(gè)LockingPtr<BufT>;來(lái)指向它。當(dāng)你這樣做了以后,你就
            可以訪問vector的全部接口。

            這個(gè)方法的好處是,如果你犯了錯(cuò)誤,編譯器會(huì)指出它:  


            void SyncBuf::Thread2() {
                // Error! Cannot access 'begin' for a volatile object
                BufT::iterator i = buffer_.begin();
                // Error! Cannot access 'end' for a volatile object
                for (; i != lpBuf->;end(); ++i) {
                    ... use *i ...
                }
            }

            你不能訪問buffer_的任何函數(shù),除非你進(jìn)行了const_cast或者用LockingPtr。這兩者的區(qū)別是LockingPtr提供了一個(gè)有規(guī)則的方法來(lái)對(duì)一個(gè)volatile變量進(jìn)行const_cast。  
            LockingPtr有非常好的表達(dá)力。如果你只需要調(diào)用一個(gè)函數(shù),你可以創(chuàng)建一個(gè)無(wú)名的臨時(shí)LockingPtr對(duì)象,然后直接使用它:  

            unsigned int SyncBuf::Size() {

                return LockingPtr<BufT>;(buffer_, mtx_)->;size();

            }


            回到原生類型 
            我們已經(jīng)看到了volatile對(duì)于保護(hù)對(duì)象免于不受控的訪問是多么出色,并且看到了LockingPtr是怎么提供了一個(gè)簡(jiǎn)單有效的辦法來(lái)編寫線程安全的代碼。現(xiàn)在讓我們回到原生類型,volatile對(duì)它們的作用方式是不同的。 

            讓我們來(lái)考慮一個(gè)多個(gè)線程共享一個(gè)int變量的例子。  


            class Counter
            {
            public:
                ...
                void Increment() { ++ctr_; }
                void Decrement() { --ctr_; }
            private:
                int ctr_;
            };


            如果Increment和Decrement是在不同的線程里被調(diào)用的,上面的代碼片斷里就有bug。首先,ctr_必須是volatile的。其次,即使是一個(gè)看上去是原子的操作,比如++ctr_,實(shí)際上也分為三個(gè)階段。內(nèi)存本身是沒有運(yùn)算功能的,當(dāng)對(duì)一個(gè)變量進(jìn)行增量操作時(shí),處理器會(huì):  
            把變量讀入寄存器  
            對(duì)寄存器里的值加1  
            把結(jié)果寫回內(nèi)存  
            這個(gè)三步操作稱為RMW(Read-Modify-Write)。在一個(gè)RMW操作的Modify階段,大多數(shù)處理器都會(huì)釋放內(nèi)存總線,以使其他處理器能夠訪問內(nèi)存。  
            如果在這個(gè)時(shí)候另一個(gè)處理器對(duì)同一個(gè)變量也進(jìn)行RMW操作,我們就遇到了一個(gè)競(jìng)爭(zhēng)條件:第二次寫入會(huì)覆蓋掉第一次的值。  

            為了防止這樣的事發(fā)生,你又要用到LockingPtr:  

            class Counter
            
            {
            public:
                ...
                void Increment() { ++*LockingPtr<int>;(ctr_, mtx_); }
                void Decrement() { --*LockingPtr<int>;(ctr_, mtx_); }
            private:
                volatile int ctr_;
                Mutex mtx_;
            };

            現(xiàn)在這段代碼正確了,但是和SyncBuf相比,這段代碼的質(zhì)量要差一些。為什么?因?yàn)閷?duì)于Counter,編譯器不會(huì)在你錯(cuò)誤地直接訪問ctr_(沒有對(duì)它加鎖)時(shí)產(chǎn)生警告。雖然ctr_是volatile的,但是編譯器還是可以編譯++ctr_,盡管產(chǎn)生的代碼絕對(duì)是不正確的。編譯器不再是你的盟友了,你只有自己留意競(jìng)爭(zhēng)條件。  
            那么你該怎么做呢?很簡(jiǎn)單,你可以用一個(gè)高層的結(jié)構(gòu)來(lái)包裝原生類型的數(shù)據(jù),然后對(duì)那個(gè)結(jié)構(gòu)使用volatile。這有點(diǎn)自相矛盾,直接用volatile修飾原生類型是一個(gè)不好的用法,盡管這是volatile最初期望的用法!
            到現(xiàn)在為止,我們討論了具有volatile數(shù)據(jù)成員的類;現(xiàn)在讓我們來(lái)考慮設(shè)計(jì)這樣的類,它會(huì)作為更大的對(duì)象的一部分并且在線程間共享。這里,volatile的成員函數(shù)可以幫很大的忙。  

            在設(shè)計(jì)類的時(shí)候,你只對(duì)那些線程安全的成員函數(shù)加volatile修飾。你必須假定外面的代碼會(huì)在任何地方任何時(shí)間調(diào)用volatile成員函數(shù)。不要忘記:volatile相當(dāng)于自由的多線程代碼,并且沒有臨界區(qū);非volatile相當(dāng)于單線程的環(huán)境或者在臨界區(qū)內(nèi)。  

            比如說(shuō),你定義了一個(gè)Widget類,它用兩個(gè)方法實(shí)現(xiàn)了同一個(gè)操作——一個(gè)線程安全的方法和一個(gè)快速的不受保護(hù)的方法。

            class Widget
            
            {
            public:
                void Operation() volatile;
                void Operation();
                ...
            private:
                Mutex mtx_;
            };


            注意這里的重載(overloading)用法。現(xiàn)在Widget的用戶可以用一致的語(yǔ)法調(diào)用Operation,對(duì)于volatile對(duì)象可以得到線程安全性,對(duì)于普通對(duì)象可以得到速度。用戶必須注意把共享的Widget對(duì)象定義為volatile。  
            在實(shí)現(xiàn)volatile成員函數(shù)時(shí),第一個(gè)操作通常是用LockingPtr對(duì)this進(jìn)行加鎖,然后其余工作可以交給非volatile的同名函數(shù)做: 

            void Widget::Operation() volatile
            
            {
                LockingPtr<Widget>; lpThis(*this, mtx_);
                lpThis->;Operation(); // invokes the non-volatile function
            }

            小結(jié) 
            在編寫對(duì)線程程序的時(shí)候,使用volatile將對(duì)你十分有益。你必須堅(jiān)持下面的規(guī)則:  

            把所有共享對(duì)象聲明為volatile  
            不要對(duì)原生類型直接使用volatile  
            定義共享類時(shí),用volatile成員函數(shù)來(lái)表示它的線程安全性。  
            如果你這么做了,而且用了簡(jiǎn)單的通用組件LockingPtr,你就可以寫出線程安全的代碼,并且大大減少對(duì)競(jìng)爭(zhēng)條件的擔(dān)心,因?yàn)榫幾g器會(huì)替你操心,并且勤勤懇懇地為你指出哪里錯(cuò)了。  

            在我參與的幾個(gè)項(xiàng)目中,使用volatile和LockingPtr產(chǎn)生了很大效果。代碼十分整潔,也容易理解。我記得遇到過(guò)一些死鎖的情況,但是相對(duì)于競(jìng)爭(zhēng)條件,我寧愿對(duì)付死鎖的情況,因?yàn)樗鼈冋{(diào)試起來(lái)容易多了。那些項(xiàng)目實(shí)際上根本沒有碰到過(guò)有關(guān)競(jìng)爭(zhēng)條件的問題。  

            致謝 
            非常感謝James Kanze和Sorin Jianu提供了很有洞察力的意見。
            致謝 
            非常感謝James Kanze和Sorin Jianu提供了很有洞察力的意見。  

            附:濫用volatile的本質(zhì)?[2] 
            在上一期的專欄《Generic<Programming>;: volatile — Multithreaded Programmer's Best Friend》發(fā)表以后,我收到很多反饋意見。就像是注定的一樣,大部分稱贊都是私人信件,而抱怨都發(fā)到USENET新聞組comp.lang.c++.moderated和 comp.programming.threads里去了。隨后引起了很長(zhǎng)很激烈的討論,如果你對(duì)這個(gè)主題有興趣,你可以去看看這個(gè)討論,它的標(biāo)題是“volatile, was: memory visibility between threads.”。  

            我知道我從這個(gè)討論中學(xué)到了很多東西。比如說(shuō),文章開頭的Widget的例子不太切題。長(zhǎng)話短說(shuō),在很多系統(tǒng)(比如POSIX兼容的系統(tǒng))中,volatile修飾是不需要的,而在另一些系統(tǒng)中,即使加了volatile也沒有用,程序還是不正確。  

            關(guān)于volatile correctness,最重要的一個(gè)問題是它依賴于類似POSIX的mutex,如果在多處理器系統(tǒng)上,光靠mutex就不夠了——你必須用memory barriers。  

            另一個(gè)更哲理性的問題是:嚴(yán)格來(lái)說(shuō)通過(guò)類型轉(zhuǎn)換把變量的volatile屬性去掉是不合法的,即使volatile屬性是你自己為了volatile correctness而加上去的。正如Anthony Williams指出的,可以想象一個(gè)系統(tǒng)可能把volatile數(shù)據(jù)放在一個(gè)不同于非volatile數(shù)據(jù)的存儲(chǔ)區(qū)中,在這種情況下,進(jìn)行地址變換會(huì)有不確定的行為。 

            另一個(gè)批評(píng)是volatile correctness雖然可以在一個(gè)較低層次上解決競(jìng)爭(zhēng)條件,但是不能正確的檢測(cè)出高層的、邏輯的競(jìng)爭(zhēng)條件。例如,你有一個(gè)mt_vector模版類,用來(lái)模擬std::vector,成員函數(shù)經(jīng)過(guò)正確的線程同步修正。考慮這段代碼:  

            volatile mt_vector<int>; vec; 
             
            if (!vec.empty()) { 
                vec.pop_back(); 
            } 

            這段代碼的目的是刪除vector里的最后一個(gè)元素,如果它存在的話。在單線程環(huán)境里,他工作地很好。然而如果你把它用在多線程程序里,這段代碼還是有可能拋出異常,盡管empty和pop_back都有正確的線程同步行為。雖然底層的數(shù)據(jù)(vec)的一致性有保證,但是高層操作的結(jié)果還是不確定的。  
            無(wú)論如何,經(jīng)過(guò)辯論之后,我還是保持我的建議,在有類POSIX的mutex的系統(tǒng)上,volatile correctness還是檢測(cè)競(jìng)爭(zhēng)條件的一個(gè)有價(jià)值的工具。但是如果你在一個(gè)支持內(nèi)存訪問重新排序的多處理器系統(tǒng)上,你首先需要仔細(xì)閱讀你的編譯器的文檔。你必須知己知彼。
            最后,Kenneth Chiu提到了一篇非常有趣的文章http://theory.stanford.edu/~freunds/race.ps,猜猜題目是什么?“Type-Based Race Detection for Java”。這篇文章講了怎么對(duì)Java的類型系統(tǒng)作一點(diǎn)小小的補(bǔ)充,從而讓編譯器和程序員一起在編譯時(shí)檢測(cè)競(jìng)爭(zhēng)條件。  

            Eppur si muove.[3] 
            譯注 
            [1] Volatile原意為易變的,反復(fù)無(wú)常的。  

            [2] 這是Andrei在他的下一篇Generic<Programming>;里對(duì)本文作的補(bǔ)充說(shuō)明。  

            [3] 這是伽利略在被迫放棄他一直信仰的哥白尼的地動(dòng)說(shuō)時(shí)所說(shuō)過(guò)的一句辯解的話,意思是“它(地球)畢竟仍在運(yùn)動(dòng)”。

            posted on 2011-04-05 17:41 沛沛 閱讀(442) 評(píng)論(0)  編輯 收藏 引用 所屬分類: C++
            国产亚洲精久久久久久无码77777 国产亚洲精品久久久久秋霞 | 久久人人爽人人人人爽AV| 久久久久噜噜噜亚洲熟女综合 | 性做久久久久久久| 久久午夜电影网| 亚洲国产精品无码久久青草 | 色狠狠久久AV五月综合| 日本久久久精品中文字幕| 色悠久久久久久久综合网| 精品久久8x国产免费观看| 久久久久亚洲精品天堂久久久久久| 久久精品一区二区三区AV| 99久久国产综合精品网成人影院| 伊人久久一区二区三区无码| 国产成人精品白浆久久69| 国内精品久久久久影院亚洲| 99久久精品免费看国产免费| 亚洲乱码精品久久久久..| 亚洲欧美日韩久久精品| 91久久精品视频| 99久久免费国产精精品| 热re99久久6国产精品免费| 欧美午夜精品久久久久久浪潮| 久久96国产精品久久久| 精品久久久无码人妻中文字幕| 久久e热在这里只有国产中文精品99| 日韩精品久久久肉伦网站| 久久综合久久综合亚洲| 久久综合日本熟妇| 久久精品国产亚洲Aⅴ香蕉| 精品久久久久久久久久久久久久久| 77777亚洲午夜久久多喷| 国产亚洲综合久久系列| 婷婷久久香蕉五月综合加勒比| 国内精品久久国产| 亚洲AV无码久久精品色欲| 亚洲国产美女精品久久久久∴| 亚洲午夜久久久久久噜噜噜| 99久久精品免费看国产一区二区三区| 亚洲精品无码久久毛片| 国内精品综合久久久40p|