日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

.NET简谈组件程序设计之(手动同步)

發(fā)布時(shí)間:2025/3/20 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET简谈组件程序设计之(手动同步) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在上一篇文章“.NET簡(jiǎn)談組件程序設(shè)計(jì)之(上下文與同步域) ”中,我們學(xué)習(xí)了關(guān)于一些上下文和同步域的概念,可以利用這兩個(gè)技術(shù)來(lái)進(jìn)行自動(dòng)同步。

今天我們主要學(xué)習(xí)怎么手動(dòng)來(lái)執(zhí)行同步,能從更小的粒度進(jìn)行封鎖,以達(dá)到最大程度的吞吐量。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]

我們知道線程是進(jìn)程的運(yùn)行實(shí)體,進(jìn)程是資源分配單位,而線程是執(zhí)行單位。照書(shū)上所說(shuō),線程是程序的執(zhí)行路徑,當(dāng)我們分配一個(gè)線程的時(shí)候,要確定線程的執(zhí)行路徑是什么,也就是代碼中的ThreadStart委托所指向的入口點(diǎn)方法。

一旦我們手動(dòng)Start啟動(dòng)線程的時(shí)候,當(dāng)前上下文線程就被系統(tǒng)立即切換,我們從Thread.CurrentThread靜態(tài)屬性中可以準(zhǔn)確的獲取到當(dāng)前正在執(zhí)行的線程實(shí)例,然后進(jìn)行一系列的設(shè)置、等待、終止。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]

那么線程到底是怎么實(shí)現(xiàn)同步(互斥)的呢,在我們正常的代碼中是沒(méi)有關(guān)于線程同步的現(xiàn)實(shí)代碼的,所以在線程執(zhí)行路徑中是不存在任何能夠阻塞線程的實(shí)現(xiàn)代碼。要想實(shí)現(xiàn)線程阻塞,就必須在線程的執(zhí)行路徑中寫(xiě)點(diǎn)東西,讓所有線程當(dāng)進(jìn)入這段代碼的時(shí)候(也就是臨界資源),通過(guò)判斷某種東西來(lái)確定是否允許進(jìn)入執(zhí)行。

圖1:

在ThreadStartEnterPoint方法中,如果沒(méi)有任何的同步代碼,那么任何線程都能進(jìn)去執(zhí)行,就導(dǎo)致了亂七八糟的數(shù)據(jù)。當(dāng)數(shù)據(jù)在內(nèi)存中的時(shí)候,在同一時(shí)間只能是由CPU去執(zhí)行線程的代碼,但是線程是有競(jìng)爭(zhēng)情況的,當(dāng)線程1還沒(méi)有完全執(zhí)行完畢,線程2就來(lái)執(zhí)行這塊數(shù)據(jù),導(dǎo)致數(shù)據(jù)的不同步。

那么我們需要再線程1還沒(méi)有執(zhí)行完畢前不允許其他線程使用這塊內(nèi)存對(duì)象。當(dāng)線程1使用完后就立即釋放所占有的資源,讓其他線程能競(jìng)爭(zhēng)。

利用Monitor(監(jiān)視器)來(lái)進(jìn)行同步

Monitor是用來(lái)提供同步的對(duì)象,通過(guò)它可以在某個(gè)時(shí)間點(diǎn)上鎖定對(duì)象。請(qǐng)看代碼:

  • [MethodImpl(MethodImplOptions.Synchronized)] ?
  • ???????public?void?ShowMessage() ?
  • ???????{ ?
  • ???????????for?(int?i?=?0;?i?<?20;?i++) ?
  • ???????????{ ?
  • ???????????????if?(i?==?10) ?
  • ???????????????????Monitor.Wait(this);//等第二個(gè)線程使用完后,我在繼續(xù)執(zhí)行。將當(dāng)前線程放置在等待隊(duì)列里 ?
  • ???????????????Thread?currentthread?=?Thread.CurrentThread; ?
  • ???????????????Console.WriteLine(currentthread.Name?+?"|"?+?currentthread.ManagedThreadId?+?"|"?+?i.ToString()); ?
  • ???????????} ?
  • ???????} ?
  • ???????[MethodImpl(MethodImplOptions.Synchronized)] ?
  • ???????public?void?PluseThread() ?
  • ???????{ ?
  • ???????????for?(int?i?=?0;?i?<?20;?i++) ?
  • ???????????{ ?
  • ???????????????Thread?currentthread?=?Thread.CurrentThread; ?
  • ???????????????Console.WriteLine(currentthread.Name?+?"|"?+?currentthread.ManagedThreadId?+?"|"?+?i.ToString()); ?
  • ???????????} ?
  • ???????????Monitor.Pulse(this);//將等待隊(duì)列里的線程放到鎖定隊(duì)列,也就是Monitor.Enter(); ?
  • ???????}?
  • 這是一個(gè)類中的兩個(gè)方法,在方法的頭部我用了MethodImpl方法特性進(jìn)行了標(biāo)識(shí),其實(shí)這個(gè)特性的目的就是在方法的入口處和結(jié)束處加上同步方法,也就是Monitor.Enter和Monitor.Exit,一般情況下我們都是習(xí)慣用lock來(lái)鎖定對(duì)象,其實(shí)lock也是Monitor的變體。這里我就不寫(xiě)出來(lái)了。讓我們熟悉一下陌生的使用方式。

  • Myclass1?myclass?=?new?Myclass1(); ?
  • ?
  • ???????????ThreadStart?startdeleted?=?new?ThreadStart(myclass.ShowMessage); ?
  • ???????????Thread?thread?=?new?Thread(startdeleted); ?
  • ???????????thread.Name?=?"線程1"; ?
  • ???????????ThreadStart?stra?=?new?ThreadStart(myclass.PluseThread); ?
  • ???????????Thread?thread2?=?new?Thread(stra); ?
  • ???????????thread2.Name?=?"線程2"; ?
  • ?
  • ???????????thread.Start(); ?
  • ???????????thread2.Start(); ?
  • ?
  • ???????????thread2.Join(); ?
  • ???????????Console.WriteLine("線程2已經(jīng)釋放"); ?
  • ???????????thread.Join(); ?
  • ???????????Console.WriteLine("線程1已經(jīng)釋放"); ?
  • 在調(diào)用的代碼里面,大概意思是這樣的,我們同時(shí)開(kāi)啟兩個(gè)線程,入口點(diǎn)分別是上面的兩個(gè)方法,在PluseThread里面是為了將ShowMessage線程1從等待隊(duì)列里釋放出來(lái)繼續(xù)執(zhí)行。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]

    在ShowMessage里面我用Monitor.Wait方法等待,當(dāng)調(diào)用這個(gè)方法的時(shí)候會(huì)使用我鎖定的對(duì)象,讓其他線程進(jìn)入執(zhí)行。當(dāng)Monitor.Pluse的時(shí)候,線程1繼續(xù)執(zhí)行。

    靜態(tài)Monitor對(duì)象是每個(gè)線程都會(huì)執(zhí)行的路徑,我們通過(guò)控制Monitor來(lái)進(jìn)行線程同步,當(dāng)我們調(diào)用Wait就是等待,直到當(dāng)前對(duì)象Pluse才繼續(xù)執(zhí)行。

    圖2:

    利用WaitHandle(等待句柄)來(lái)進(jìn)行同步

    上面我們通過(guò)Monitor來(lái)進(jìn)行同步,在同步的時(shí)候我們需要很好的控制等待時(shí)間,用Monitor也能通過(guò)Wait進(jìn)行等待超時(shí)設(shè)置,也許它內(nèi)部封裝是Windows等待句柄。

    這里我們通過(guò)使用WaitHandle來(lái)進(jìn)行同步,WaitHandle是個(gè)抽象類,它的子類有很多,比如Mutex互斥體、ManualResetEvent、AutoResetEvent事件對(duì)象,等等。下面我們就來(lái)看看利用這些對(duì)象怎么同步線程。

    Mutext互斥體

  • public?void?Print() ?
  • ????????{ ?
  • ????????????Mutex?mutex?=?new?Mutex(false,?"myclass");//Mutex互斥體 ?
  • ????????????mutex.WaitOne(); ?
  • ????????????for?(int?i?=?0;?i?<?10;?i++) ?
  • ????????????{ ?
  • ????????????????Console.WriteLine(i.ToString()?+?Thread.CurrentThread.Name); ?
  • ????????????} ?
  • ????????????mutex.ReleaseMutex(); ?
  • ????????}?
  • 在方法的內(nèi)部我們申請(qǐng)一個(gè)Mutex對(duì)象,這個(gè)Mutex是全局的,就是在一臺(tái)機(jī)器上只能存在一個(gè)名稱的Mutex,Mutex可用來(lái)同步線程也可以用來(lái)同步進(jìn)程。

    我們?cè)赑rint方法里面用WaitOne獲取句柄,如果已經(jīng)有線程“捷足先得”了,那么這里將阻塞,并返回false。

    在使用完后,記得調(diào)用ReleaseMutex釋放當(dāng)前占用的Mutex句柄。

  • Myclass1?myclass?=?new?Myclass1(); ?
  • ?
  • ???????????Thread?thread?=?new?Thread(new?ThreadStart(myclass.Print)); ?
  • ???????????thread.Name?=?"線程1"; ?
  • ?
  • ???????????Thread?thread2?=?new?Thread(new?ThreadStart(myclass.Print)); ?
  • ???????????thread2.Name?=?"線程2"; ?
  • ?
  • ???????????thread.Start(); ?
  • ???????????thread2.Start(); ?
  • ?
  • ???????????thread2.Join(); ?
  • ???????????thread.Join(); ?
  • ???????????Console.WriteLine(Thread.CurrentThread.ManagedThreadId); ?
  • ???????????Console.ReadLine();?
  • 圖3:

    利用ManualResetEvent(手動(dòng)事件)來(lái)進(jìn)行同步

    我們直接看代碼吧,ManualResetEvent大家可能都用過(guò)。

  • public?class?EventWaitHandlerDemo ?
  • ???{ ?
  • ???????ManualResetEvent?resetevent?=?new?ManualResetEvent(false); ?
  • ???????public?EventWaitHandlerDemo() ?
  • ???????{ ?
  • ???????????Thread?thread?=?new?Thread(new?ThreadStart(this.DoWork)); ?
  • ???????????thread.Start(); ?
  • ???????} ?
  • ???????private?void?DoWork() ?
  • ???????{ ?
  • ???????????int?count?=?0; ?
  • ???????????while?(true) ?
  • ???????????{ ?
  • ???????????????resetevent.WaitOne(); ?
  • ???????????????Console.WriteLine(count++); ?
  • ???????????} ?
  • ???????} ?
  • ???????public?void?GoThread() ?
  • ???????{ ?
  • ???????????resetevent.Set(); ?
  • ???????????Console.WriteLine("線程啟動(dòng)"); ?
  • ???????} ?
  • ???????public?void?Stopthread() ?
  • ???????{ ?
  • ???????????resetevent.Reset(); ?
  • ???????????Console.WriteLine("線程暫停"); ?
  • ???????} ?
  • ???????public?void?Close() ?
  • ???????{ ?
  • ???????????resetevent.Close(); ?
  • ???????????Console.WriteLine("線程終止"); ?
  • ???????} ?
  • ???}?
  • 這種類型的等待句柄對(duì)象是完全手動(dòng)控制的,讓我們想要用的時(shí)候要記得set,想要暫停的時(shí)候就Reset,不用了就close。

  • EventWaitHandlerDemo?demo?=?new?EventWaitHandlerDemo(); ?
  • ????????????demo.GoThread(); ?
  • ?
  • ????????????Thread.Sleep(1000); ?
  • ????????????demo.Stopthread(); ?
  • ?
  • ????????????Thread.Sleep(1000); ?
  • ????????????demo.Close(); ?
  • ?
  • ????????????Console.WriteLine("程序結(jié)束");?
  • 圖4:

    利用AutoResetEvent(自動(dòng)事件)來(lái)進(jìn)行同步

    從名字上就能看出,該事件是自動(dòng)重置事件,不需要想上面那樣進(jìn)行set\reset操作。

  • public?class?AutoResetEventDemo ?
  • ????{ ?
  • ????????AutoResetEvent?autoevent?=?new?AutoResetEvent(true); ?
  • ????????public?AutoResetEventDemo() ?
  • ????????{?} ?
  • ????????public?void?Print() ?
  • ????????{ ?
  • ????????????autoevent.WaitOne(); ?
  • ????????????//??autoevent.Reset();在手動(dòng)事件中,需要手動(dòng)切換狀態(tài) ?
  • ????????????int?i?=?0; ?
  • ????????????while?(true) ?
  • ????????????{ ?
  • ????????????????if?(i?==?10) ?
  • ????????????????{ ?
  • ????????????????????Console.WriteLine(Thread.CurrentThread.Name?+?"線程結(jié)束"); ?
  • ????????????????????autoevent.Set(); ?
  • ????????????????????break; ?
  • ????????????????} ?
  • ????????????????Console.WriteLine(Thread.CurrentThread.ManagedThreadId?+?"|"?+?i++); ?
  • ????????????} ?
  • ????????} ?
  • ????}?
  • 在上面的代碼中,我們通過(guò)WaitOne獲取等待句柄,當(dāng)我們獲取到之后,事件對(duì)象會(huì)自動(dòng)重置為信號(hào)已發(fā),其他線程無(wú)法獲取到等待句柄。當(dāng)我們set之后其他線程才能獲取到,這里省掉的是線程進(jìn)入執(zhí)行路徑的過(guò)程。

    ManualResetEvent需要手動(dòng)進(jìn)行set才能使用,一旦set之后信號(hào)標(biāo)記為未發(fā)狀態(tài),所有線程都能執(zhí)行代碼,除非手動(dòng)Reset才能阻塞。

  • Console.WriteLine(Thread.CurrentThread.ManagedThreadId); ?
  • ???????????AutoResetEventDemo?autodemo?=?new?AutoResetEventDemo(); ?
  • ?
  • ???????????Thread?thread1?=?new?Thread(new?ThreadStart(autodemo.Print)); ?
  • ???????????thread1.Name?=?"線程1"; ?
  • ?
  • ???????????Thread?thread2?=?new?Thread(new?ThreadStart(autodemo.Print)); ?
  • ???????????thread2.Name?=?"線程2"; ?
  • ???????????thread1.Start(); ?
  • ???????????thread2.Start(); ?
  • ?
  • ???????????thread1.Join(); ?
  • ???????????thread2.Join(); ?
  • ???????????Console.WriteLine(Thread.CurrentThread.ManagedThreadId); ?
  • ???????????Console.Read();?
  • 圖5:

    [王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]

    與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖

    總結(jié)

    以上是生活随笔為你收集整理的.NET简谈组件程序设计之(手动同步)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。