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

            cc

              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              38 隨筆 :: 14 文章 :: 21 評(píng)論 :: 0 Trackbacks
            隨著對(duì)多線(xiàn)程學(xué)習(xí)的深入,你可能覺(jué)得需要了解一些有關(guān)線(xiàn)程共享資源的問(wèn)題. .NET framework提供了很多的類(lèi)和數(shù)據(jù)類(lèi)型來(lái)控制對(duì)共享資源的訪(fǎng)問(wèn)。

              考慮一種我們經(jīng)常遇到的情況:有一些全局變量和共享的類(lèi)變量,我們需要從不同的線(xiàn)程來(lái)更新它們,可以通過(guò)使用System.Threading.Interlocked類(lèi)完成這樣的任務(wù),它提供了原子的,非模塊化的整數(shù)更新操作。

              還有你可以使用System.Threading.Monitor類(lèi)鎖定對(duì)象的方法的一段代碼,使其暫時(shí)不能被別的線(xiàn)程訪(fǎng)問(wèn)。

              System.Threading.WaitHandle類(lèi)的實(shí)例可以用來(lái)封裝等待對(duì)共享資源的獨(dú)占訪(fǎng)問(wèn)權(quán)的操作系統(tǒng)特定的對(duì)象。尤其對(duì)于非受管代碼的互操作問(wèn)題。

              System.Threading.Mutex用于對(duì)多個(gè)復(fù)雜的線(xiàn)程同步的問(wèn)題,它也允許單線(xiàn)程的訪(fǎng)問(wèn)。

              像ManualResetEvent和AutoResetEvent這樣的同步事件類(lèi)支持一個(gè)類(lèi)通知其他事件的線(xiàn)程。

              不討論線(xiàn)程的同步問(wèn)題,等于對(duì)多線(xiàn)程編程知之甚少,但是我們要十分謹(jǐn)慎的使用多線(xiàn)程的同步。在使用線(xiàn)程同步時(shí),我們事先就要要能夠正確的確定是那個(gè)對(duì)象和方法有可能造成死鎖(死鎖就是所有的線(xiàn)程都停止了相應(yīng),都在等者對(duì)方釋放資源)。還有贓數(shù)據(jù)的問(wèn)題(指的是同一時(shí)間多個(gè)線(xiàn)程對(duì)數(shù)據(jù)作了操作而造成的不一致),這個(gè)不容易理解,這么說(shuō)吧,有X和Y兩個(gè)線(xiàn)程,線(xiàn)程X從文件讀取數(shù)據(jù)并且寫(xiě)數(shù)據(jù)到數(shù)據(jù)結(jié)構(gòu),線(xiàn)程Y從這個(gè)數(shù)據(jù)結(jié)構(gòu)讀數(shù)據(jù)并將數(shù)據(jù)送到其他的計(jì)算機(jī)。假設(shè)在Y讀數(shù)據(jù)的同時(shí),X寫(xiě)入數(shù)據(jù),那么顯然Y讀取的數(shù)據(jù)與實(shí)際存儲(chǔ)的數(shù)據(jù)是不一致的。這種情況顯然是我們應(yīng)該避免發(fā)生的。少量的線(xiàn)程將使得剛才的問(wèn)題發(fā)生的幾率要少的多,對(duì)共享資源的訪(fǎng)問(wèn)也更好的同步。

              .NET Framework的CLR提供了三種方法來(lái)完成對(duì)共享資源 ,諸如全局變量域,特定的代碼段,靜態(tài)的和實(shí)例化的方法和域。

              (1) 代碼域同步:使用Monitor類(lèi)可以同步靜態(tài)/實(shí)例化的方法的全部代碼或者部分代碼段。不支持靜態(tài)域的同步。在實(shí)例化的方法中,this指針用于同步;而在靜態(tài)的方法中,類(lèi)用于同步,這在后面會(huì)講到。

              (2) 手工同步:使用不同的同步類(lèi)(諸如WaitHandle, Mutex, ReaderWriterLock, ManualResetEvent, AutoResetEvent 和Interlocked等)創(chuàng)建自己的同步機(jī)制。這種同步方式要求你自己手動(dòng)的為不同的域和方法同步,這種同步方式也可以用于進(jìn)程間的同步和對(duì)共享資源的等待而造成的死鎖解除。

              (3) 上下文同步:使用SynchronizationAttribute為ContextBoundObject對(duì)象創(chuàng)建簡(jiǎn)單的,自動(dòng)的同步。這種同步方式僅用于實(shí)例化的方法和域的同步。所有在同一個(gè)上下文域的對(duì)象共享同一個(gè)鎖。

            Monitor Class

              在給定的時(shí)間和指定的代碼段只能被一個(gè)線(xiàn)程訪(fǎng)問(wèn),Monitor 類(lèi)非常適合于這種情況的線(xiàn)程同步。這個(gè)類(lèi)中的方法都是靜態(tài)的,所以不需要實(shí)例化這個(gè)類(lèi)。下面一些靜態(tài)的方法提供了一種機(jī)制用來(lái)同步對(duì)象的訪(fǎng)問(wèn)從而避免死鎖和維護(hù)數(shù)據(jù)的一致性。

              Monitor.Enter 方法:在指定對(duì)象上獲取排他鎖。

              Monitor.TryEnter 方法:試圖獲取指定對(duì)象的排他鎖。

              Monitor.Exit 方法:釋放指定對(duì)象上的排他鎖。

              Monitor.Wait 方法:釋放對(duì)象上的鎖并阻塞當(dāng)前線(xiàn)程,直到它重新獲取該鎖。

              Monitor.Pulse 方法:通知等待隊(duì)列中的線(xiàn)程鎖定對(duì)象狀態(tài)的更改。

              Monitor.PulseAll 方法:通知所有的等待線(xiàn)程對(duì)象狀態(tài)的更改。

              通過(guò)對(duì)指定對(duì)象的加鎖和解鎖可以同步代碼段的訪(fǎng)問(wèn)。Monitor.Enter, Monitor.TryEnter 和 Monitor.Exit用來(lái)對(duì)指定對(duì)象的加鎖和解鎖。一旦獲取(調(diào)用了Monitor.Enter)指定對(duì)象(代碼段)的鎖,其他的線(xiàn)程都不能獲取該鎖。舉個(gè)例子來(lái)說(shuō)吧,線(xiàn)程X獲得了一個(gè)對(duì)象鎖,這個(gè)對(duì)象鎖可以釋放的(調(diào)用Monitor.Exit(object) or Monitor.Wait)。當(dāng)這個(gè)對(duì)象鎖被釋放后,Monitor.Pulse方法和 Monitor.PulseAll方法通知就緒隊(duì)列的下一個(gè)線(xiàn)程進(jìn)行和其他所有就緒隊(duì)列的線(xiàn)程將有機(jī)會(huì)獲取排他鎖。線(xiàn)程X釋放了鎖而線(xiàn)程Y獲得了鎖,同時(shí)調(diào)用Monitor.Wait的線(xiàn)程X進(jìn)入等待隊(duì)列。當(dāng)從當(dāng)前鎖定對(duì)象的線(xiàn)程(線(xiàn)程Y)受到了Pulse或PulseAll,等待隊(duì)列的線(xiàn)程就進(jìn)入就緒隊(duì)列。線(xiàn)程X重新得到對(duì)象鎖時(shí),Monitor.Wait才返回。如果擁有鎖的線(xiàn)程(線(xiàn)程Y)不調(diào)用Pulse或PulseAll,方法可能被不確定的鎖定。Pulse, PulseAll and Wait必須是被同步的代碼段鄂被調(diào)用。對(duì)每一個(gè)同步的對(duì)象,你需要有當(dāng)前擁有鎖的線(xiàn)程的指針,就緒隊(duì)列和等待隊(duì)列(包含需要被通知鎖定對(duì)象的狀態(tài)變化的線(xiàn)程)的指針。

              你也許會(huì)問(wèn),當(dāng)兩個(gè)線(xiàn)程同時(shí)調(diào)用Monitor.Enter會(huì)發(fā)生什么事情?無(wú)論這兩個(gè)線(xiàn)程地調(diào)用Monitor.Enter是多么地接近,實(shí)際上肯定有一個(gè)在前,一個(gè)在后,因此永遠(yuǎn)只會(huì)有一個(gè)獲得對(duì)象鎖。既然Monitor.Enter是原子操作,那么CPU是不可能偏好一個(gè)線(xiàn)程而不喜歡另外一個(gè)線(xiàn)程的。為了獲取更好的性能,你應(yīng)該延遲后一個(gè)線(xiàn)程的獲取鎖調(diào)用和立即釋放前一個(gè)線(xiàn)程的對(duì)象鎖。對(duì)于private和internal的對(duì)象,加鎖是可行的,但是對(duì)于external對(duì)象有可能導(dǎo)致死鎖,因?yàn)椴幌嚓P(guān)的代碼可能因?yàn)椴煌哪康亩鴮?duì)同一個(gè)對(duì)象加鎖。

              如果你要對(duì)一段代碼加鎖,最好的是在try語(yǔ)句里面加入設(shè)置鎖的語(yǔ)句,而將Monitor.Exit放在finally語(yǔ)句里面。對(duì)于整個(gè)代碼段的加鎖,你可以使用MethodImplAttribute(在System.Runtime.CompilerServices命名空間)類(lèi)在其構(gòu)造器中設(shè)置同步值。這是一種可以替代的方法,當(dāng)加鎖的方法返回時(shí),鎖也就被釋放了。如果需要要很快釋放鎖,你可以使用Monitor類(lèi)和C# lock的聲明代替上述的方法。

              讓我們來(lái)看一段使用Monitor類(lèi)的代碼:

            public void some_method()
            {

            int a=100;

            int b=0;

            Monitor.Enter(this);

            //say we do something here.

            int c=a/b;

            Monitor.Exit(this);

            }


              上面的代碼運(yùn)行會(huì)產(chǎn)生問(wèn)題。當(dāng)代碼運(yùn)行到int c=a/b; 的時(shí)候,會(huì)拋出一個(gè)異常,Monitor.Exit將不會(huì)返回。因此這段程序?qū)炱穑渌木€(xiàn)程也將得不到鎖。有兩種方法可以解決上面的問(wèn)題。第一個(gè)方法是:將代碼放入try…finally內(nèi),在finally調(diào)用Monitor.Exit,這樣的話(huà)最后一定會(huì)釋放鎖。第二種方法是:利用C#的lock()方法。調(diào)用這個(gè)方法和調(diào)用Monitoy.Enter的作用效果是一樣的。但是這種方法一旦代碼執(zhí)行超出范圍,釋放鎖將不會(huì)自動(dòng)的發(fā)生。見(jiàn)下面的代碼:

            public void some_method()
            {

            int a=100;

            int b=0;

            lock(this);

            //say we do something here.

            int c=a/b;

            }


              C# lock申明提供了與Monitoy.Enter和Monitoy.Exit同樣的功能,這種方法用在你的代碼段不能被其他獨(dú)立的線(xiàn)程中斷的情況。

            WaitHandle Class

              WaitHandle類(lèi)作為基類(lèi)來(lái)使用的,它允許多個(gè)等待操作。這個(gè)類(lèi)封裝了win32的同步處理方法。WaitHandle對(duì)象通知其他的線(xiàn)程它需要對(duì)資源排他性的訪(fǎng)問(wèn),其他的線(xiàn)程必須等待,直到WaitHandle不再使用資源和等待句柄沒(méi)有被使用。下面是從它繼承來(lái)的幾個(gè)類(lèi):

              Mutex 類(lèi):同步基元也可用于進(jìn)程間同步。

              AutoResetEvent:通知一個(gè)或多個(gè)正在等待的線(xiàn)程已發(fā)生事件。無(wú)法繼承此類(lèi)。

              ManualResetEvent:當(dāng)通知一個(gè)或多個(gè)正在等待的線(xiàn)程事件已發(fā)生時(shí)出現(xiàn)。無(wú)法繼承此類(lèi)。

              這些類(lèi)定義了一些信號(hào)機(jī)制使得對(duì)資源排他性訪(fǎng)問(wèn)的占有和釋放。他們有兩種狀態(tài):signaled 和 nonsignaled。Signaled狀態(tài)的等待句柄不屬于任何線(xiàn)程,除非是nonsignaled狀態(tài)。擁有等待句柄的線(xiàn)程不再使用等待句柄時(shí)用set方法,其他的線(xiàn)程可以調(diào)用Reset方法來(lái)改變狀態(tài)或者任意一個(gè)WaitHandle方法要求擁有等待句柄,這些方法見(jiàn)下面:

              WaitAll:等待指定數(shù)組中的所有元素收到信號(hào)。

              WaitAny:等待指定數(shù)組中的任一元素收到信號(hào)。

              WaitOne:當(dāng)在派生類(lèi)中重寫(xiě)時(shí),阻塞當(dāng)前線(xiàn)程,直到當(dāng)前的 WaitHandle 收到信號(hào)。

              這些wait方法阻塞線(xiàn)程直到一個(gè)或者更多的同步對(duì)象收到信號(hào)。

              WaitHandle對(duì)象封裝等待對(duì)共享資源的獨(dú)占訪(fǎng)問(wèn)權(quán)的操作系統(tǒng)特定的對(duì)象無(wú)論是收管代碼還是非受管代碼都可以使用。但是它沒(méi)有Monitor使用輕便,Monitor是完全的受管代碼而且對(duì)操作系統(tǒng)資源的使用非常有效率。


            Mutex Class

              Mutex是另外一種完成線(xiàn)程間和跨進(jìn)程同步的方法,它同時(shí)也提供進(jìn)程間的同步。它允許一個(gè)線(xiàn)程獨(dú)占共享資源的同時(shí)阻止其他線(xiàn)程和進(jìn)程的訪(fǎng)問(wèn)。Mutex的名字就很好的說(shuō)明了它的所有者對(duì)資源的排他性的占有。一旦一個(gè)線(xiàn)程擁有了Mutex,想得到Mutex的其他線(xiàn)程都將掛起直到占有線(xiàn)程釋放它。Mutex.ReleaseMutex方法用于釋放Mutex,一個(gè)線(xiàn)程可以多次調(diào)用wait方法來(lái)請(qǐng)求同一個(gè)Mutex,但是在釋放Mutex的時(shí)候必須調(diào)用同樣次數(shù)的Mutex.ReleaseMutex。如果沒(méi)有線(xiàn)程占有Mutex,那么Mutex的狀態(tài)就變?yōu)閟ignaled,否則為nosignaled。一旦Mutex的狀態(tài)變?yōu)閟ignaled,等待隊(duì)列的下一個(gè)線(xiàn)程將會(huì)得到Mutex。Mutex類(lèi)對(duì)應(yīng)與win32的CreateMutex,創(chuàng)建Mutex對(duì)象的方法非常簡(jiǎn)單,常用的有下面幾種方法:

              一個(gè)線(xiàn)程可以通過(guò)調(diào)用WaitHandle.WaitOne 或 WaitHandle.WaitAny 或 WaitHandle.WaitAll得到Mutex的擁有權(quán)。如果Mutex不屬于任何線(xiàn)程,上述調(diào)用將使得線(xiàn)程擁有Mutex,而且WaitOne會(huì)立即返回。但是如果有其他的線(xiàn)程擁有Mutex,WaitOne將陷入無(wú)限期的等待直到獲取Mutex。你可以在WaitOne方法中指定參數(shù)即等待的時(shí)間而避免無(wú)限期的等待Mutex。調(diào)用Close作用于Mutex將釋放擁有。一旦Mutex被創(chuàng)建,你可以通過(guò)GetHandle方法獲得Mutex的句柄而給WaitHandle.WaitAny 或 WaitHandle.WaitAll 方法使用。

              下面是一個(gè)示例:

            public void some_method()
            {

            int a=100;

            int b=20;

            Mutex firstMutex = new Mutex(false);

            FirstMutex.WaitOne();

            //some kind of processing can be done here.

            Int x=a/b;

            FirstMutex.Close();

            }


              在上面的例子中,線(xiàn)程創(chuàng)建了Mutex,但是開(kāi)始并沒(méi)有申明擁有它,通過(guò)調(diào)用WaitOne方法擁有Mutex。

            Synchronization Events

              同步時(shí)間是一些等待句柄用來(lái)通知其他的線(xiàn)程發(fā)生了什么事情和資源是可用的。他們有兩個(gè)狀態(tài):signaled and nonsignaled。AutoResetEvent 和 ManualResetEvent就是這種同步事件。


            AutoResetEvent Class

              這個(gè)類(lèi)可以通知一個(gè)或多個(gè)線(xiàn)程發(fā)生事件。當(dāng)一個(gè)等待線(xiàn)程得到釋放時(shí),它將狀態(tài)轉(zhuǎn)換為signaled。用set方法使它的實(shí)例狀態(tài)變?yōu)閟ignaled。但是一旦等待的線(xiàn)程被通知時(shí)間變?yōu)閟ignaled,它的轉(zhuǎn)臺(tái)將自動(dòng)的變?yōu)閚onsignaled。如果沒(méi)有線(xiàn)程偵聽(tīng)事件,轉(zhuǎn)臺(tái)將保持為signaled。此類(lèi)不能被繼承。


            ManualResetEvent Class

              這個(gè)類(lèi)也用來(lái)通知一個(gè)或多個(gè)線(xiàn)程事件發(fā)生了。它的狀態(tài)可以手動(dòng)的被設(shè)置和重置。手動(dòng)重置時(shí)間將保持signaled狀態(tài)直到ManualResetEvent.Reset設(shè)置其狀態(tài)為nonsignaled,或保持狀態(tài)為nonsignaled直到ManualResetEvent.Set設(shè)置其狀態(tài)為signaled。這個(gè)類(lèi)不能被繼承。


            Interlocked Class

              它提供了在線(xiàn)程之間共享的變量訪(fǎng)問(wèn)的同步,它的操作時(shí)原子操作,且被線(xiàn)程共享.你可以通過(guò)Interlocked.Increment 或 Interlocked.Decrement來(lái)增加或減少共享變量.它的有點(diǎn)在于是原子操作,也就是說(shuō)這些方法可以代一個(gè)整型的參數(shù)增量并且返回新的值,所有的操作就是一步.你也可以使用它來(lái)指定變量的值或者檢查兩個(gè)變量是否相等,如果相等,將用指定的值代替其中一個(gè)變量的值.


            ReaderWriterLock class

              它定義了一種鎖,提供唯一寫(xiě)/多讀的機(jī)制,使得讀寫(xiě)的同步.任意數(shù)目的線(xiàn)程都可以讀數(shù)據(jù),數(shù)據(jù)鎖在有線(xiàn)程更新數(shù)據(jù)時(shí)將是需要的.讀的線(xiàn)程可以獲取鎖,當(dāng)且僅當(dāng)這里沒(méi)有寫(xiě)的線(xiàn)程.當(dāng)沒(méi)有讀線(xiàn)程和其他的寫(xiě)線(xiàn)程時(shí),寫(xiě)線(xiàn)程可以得到鎖.因此,一旦writer-lock被請(qǐng)求,所有的讀線(xiàn)程將不能讀取數(shù)據(jù)直到寫(xiě)線(xiàn)程訪(fǎng)問(wèn)完畢.它支持暫停而避免死鎖.它也支持嵌套的讀/寫(xiě)鎖.支持嵌套的讀鎖的方法是ReaderWriterLock.AcquireReaderLock,如果一個(gè)線(xiàn)程有寫(xiě)鎖則該線(xiàn)程將暫停;

              支持嵌套的寫(xiě)鎖的方法是ReaderWriterLock.AcquireWriterLock,如果一個(gè)線(xiàn)程有讀鎖則該線(xiàn)程暫停.如果有讀鎖將容易倒是死鎖.安全的辦法是使用ReaderWriterLock.UpgradeToWriterLock方法,這將使讀者升級(jí)到寫(xiě)者.你可以用ReaderWriterLock.DowngradeFromWriterLock方法使寫(xiě)者降級(jí)為讀者.調(diào)用ReaderWriterLock.ReleaseLock將釋放鎖, ReaderWriterLock.RestoreLock將重新裝載鎖的狀態(tài)到調(diào)用ReaderWriterLock.ReleaseLock以前.


            結(jié)論:

              這部分講述了.NET平臺(tái)上的線(xiàn)程同步的問(wèn)題.造接下來(lái)的系列文章中我將給出一些例子來(lái)更進(jìn)一步的說(shuō)明這些使用的方法和技巧.雖然線(xiàn)程同步的使用會(huì)給我們的程序帶來(lái)很大的價(jià)值,但是我們最好能夠小心使用這些方法.否則帶來(lái)的不是受益,而將倒是性能下降甚至程序崩潰.只有大量的聯(lián)系和體會(huì)才能使你駕馭這些技巧.盡量少使用那些在同步代碼塊完成不了或者不確定的阻塞的東西,尤其是I/O操作;盡可能的使用局部變量來(lái)代替全局變量;同步用在那些部分代碼被多個(gè)線(xiàn)程和進(jìn)程訪(fǎng)問(wèn)和狀態(tài)被不同的進(jìn)程共享的地方;安排你的代碼使得每一個(gè)數(shù)據(jù)在一個(gè)線(xiàn)程里得到精確的控制;不是共享在線(xiàn)程之間的代碼是安全的;在下一篇文章中我們將學(xué)習(xí)線(xiàn)程池有關(guān)的知識(shí).

            posted on 2006-12-07 15:05 醒目西西 閱讀(129) 評(píng)論(0)  編輯 收藏 引用

            只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            国产成人无码精品久久久性色 | 色偷偷88888欧美精品久久久| 久久人人爽人人精品视频| 热RE99久久精品国产66热| 亚洲乱码精品久久久久..| 91久久精品国产成人久久| 精品国产乱码久久久久软件| 国产精品久久久久久久久| 亚洲国产精品嫩草影院久久 | 91久久精品国产免费直播| 久久亚洲AV无码精品色午夜麻豆| 国产精品视频久久久| 亚洲人成电影网站久久| 国产精品无码久久四虎| 亚洲综合日韩久久成人AV| 一级做a爱片久久毛片| 久久无码人妻一区二区三区午夜| 婷婷久久综合| 午夜精品久久久久9999高清| 国产精品免费久久久久电影网| 无码精品久久久久久人妻中字| 亚洲国产精品无码久久久久久曰| 国产成人精品久久综合| 久久国产乱子精品免费女| 国产V综合V亚洲欧美久久| 久久精品国产精品亚洲精品| 思思久久99热免费精品6| 青青草国产97免久久费观看| 国产成人精品久久综合 | 久久久久18| 久久精品国产精品亜洲毛片| 99久久成人18免费网站| 久久精品国产一区| 91精品久久久久久无码| 精品熟女少妇aⅴ免费久久| 国产福利电影一区二区三区久久久久成人精品综合 | 伊人久久综合精品无码AV专区| 亚洲人成无码www久久久| 久久成人国产精品一区二区| 亚洲人成无码www久久久| 狠狠色婷婷久久综合频道日韩 |