日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

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

發布時間:2025/3/20 asp.net 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET简谈组件程序设计之(手动同步) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在上一篇文章“.NET簡談組件程序設計之(上下文與同步域) ”中,我們學習了關于一些上下文和同步域的概念,可以利用這兩個技術來進行自動同步。

今天我們主要學習怎么手動來執行同步,能從更小的粒度進行封鎖,以達到最大程度的吞吐量。[王清培版權所有,轉載請給出署名]

我們知道線程是進程的運行實體,進程是資源分配單位,而線程是執行單位。照書上所說,線程是程序的執行路徑,當我們分配一個線程的時候,要確定線程的執行路徑是什么,也就是代碼中的ThreadStart委托所指向的入口點方法。

一旦我們手動Start啟動線程的時候,當前上下文線程就被系統立即切換,我們從Thread.CurrentThread靜態屬性中可以準確的獲取到當前正在執行的線程實例,然后進行一系列的設置、等待、終止。[王清培版權所有,轉載請給出署名]

那么線程到底是怎么實現同步(互斥)的呢,在我們正常的代碼中是沒有關于線程同步的現實代碼的,所以在線程執行路徑中是不存在任何能夠阻塞線程的實現代碼。要想實現線程阻塞,就必須在線程的執行路徑中寫點東西,讓所有線程當進入這段代碼的時候(也就是臨界資源),通過判斷某種東西來確定是否允許進入執行。

圖1:

在ThreadStartEnterPoint方法中,如果沒有任何的同步代碼,那么任何線程都能進去執行,就導致了亂七八糟的數據。當數據在內存中的時候,在同一時間只能是由CPU去執行線程的代碼,但是線程是有競爭情況的,當線程1還沒有完全執行完畢,線程2就來執行這塊數據,導致數據的不同步。

那么我們需要再線程1還沒有執行完畢前不允許其他線程使用這塊內存對象。當線程1使用完后就立即釋放所占有的資源,讓其他線程能競爭。

利用Monitor(監視器)來進行同步

Monitor是用來提供同步的對象,通過它可以在某個時間點上鎖定對象。請看代碼:

  • [MethodImpl(MethodImplOptions.Synchronized)] ?
  • ???????public?void?ShowMessage() ?
  • ???????{ ?
  • ???????????for?(int?i?=?0;?i?<?20;?i++) ?
  • ???????????{ ?
  • ???????????????if?(i?==?10) ?
  • ???????????????????Monitor.Wait(this);//等第二個線程使用完后,我在繼續執行。將當前線程放置在等待隊列里 ?
  • ???????????????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);//將等待隊列里的線程放到鎖定隊列,也就是Monitor.Enter(); ?
  • ???????}?
  • 這是一個類中的兩個方法,在方法的頭部我用了MethodImpl方法特性進行了標識,其實這個特性的目的就是在方法的入口處和結束處加上同步方法,也就是Monitor.Enter和Monitor.Exit,一般情況下我們都是習慣用lock來鎖定對象,其實lock也是Monitor的變體。這里我就不寫出來了。讓我們熟悉一下陌生的使用方式。

  • 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已經釋放"); ?
  • ???????????thread.Join(); ?
  • ???????????Console.WriteLine("線程1已經釋放"); ?
  • 在調用的代碼里面,大概意思是這樣的,我們同時開啟兩個線程,入口點分別是上面的兩個方法,在PluseThread里面是為了將ShowMessage線程1從等待隊列里釋放出來繼續執行。[王清培版權所有,轉載請給出署名]

    在ShowMessage里面我用Monitor.Wait方法等待,當調用這個方法的時候會使用我鎖定的對象,讓其他線程進入執行。當Monitor.Pluse的時候,線程1繼續執行。

    靜態Monitor對象是每個線程都會執行的路徑,我們通過控制Monitor來進行線程同步,當我們調用Wait就是等待,直到當前對象Pluse才繼續執行。

    圖2:

    利用WaitHandle(等待句柄)來進行同步

    上面我們通過Monitor來進行同步,在同步的時候我們需要很好的控制等待時間,用Monitor也能通過Wait進行等待超時設置,也許它內部封裝是Windows等待句柄。

    這里我們通過使用WaitHandle來進行同步,WaitHandle是個抽象類,它的子類有很多,比如Mutex互斥體、ManualResetEvent、AutoResetEvent事件對象,等等。下面我們就來看看利用這些對象怎么同步線程。

    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(); ?
  • ????????}?
  • 在方法的內部我們申請一個Mutex對象,這個Mutex是全局的,就是在一臺機器上只能存在一個名稱的Mutex,Mutex可用來同步線程也可以用來同步進程。

    我們在Print方法里面用WaitOne獲取句柄,如果已經有線程“捷足先得”了,那么這里將阻塞,并返回false。

    在使用完后,記得調用ReleaseMutex釋放當前占用的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(手動事件)來進行同步

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

  • 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("線程啟動"); ?
  • ???????} ?
  • ???????public?void?Stopthread() ?
  • ???????{ ?
  • ???????????resetevent.Reset(); ?
  • ???????????Console.WriteLine("線程暫停"); ?
  • ???????} ?
  • ???????public?void?Close() ?
  • ???????{ ?
  • ???????????resetevent.Close(); ?
  • ???????????Console.WriteLine("線程終止"); ?
  • ???????} ?
  • ???}?
  • 這種類型的等待句柄對象是完全手動控制的,讓我們想要用的時候要記得set,想要暫停的時候就Reset,不用了就close。

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

    利用AutoResetEvent(自動事件)來進行同步

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

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

    ManualResetEvent需要手動進行set才能使用,一旦set之后信號標記為未發狀態,所有線程都能執行代碼,除非手動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:

    [王清培版權所有,轉載請給出署名]

    與50位技術專家面對面20年技術見證,附贈技術全景圖

    總結

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

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。