亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#實現多線程的同步方法實例分析

2020-01-24 02:02:51
字體:
來源:轉載
供稿:網友

本文主要描述在C#中線程同步的方法。線程的基本概念網上資料也很多就不再贅述了。直接接入 主題,在多線程開發的應用中,線程同步是不可避免的。在.Net框架中,實現線程同步主要通過以下的幾種方式來實現,在MSDN的線程指南中已經講了幾種,這里結合作者實際中用到的方式一起說明一下。

1. 維護自由鎖(InterLocked)實現同步

2. 監視器(Monitor)和互斥鎖(lock)

3. 讀寫鎖(ReadWriteLock)

4. 系統內核對象

1) 互斥(Mutex), 信號量(Semaphore), 事件(AutoResetEvent/ManualResetEvent)

2) 線程池

除了以上的這些對象之外實現線程同步的還可以使用Thread.Join方法。這種方法比較簡單,當你在第一個線程運行時想等待第二個線程執行結果,那么你可以讓第二個線程Join進來就可以了。

自由鎖(InterLocked)

對一個32位的整型數進行遞增和遞減操作來實現鎖,有人會問為什么不用++或--來 操作。因為在多線程中對鎖進行操作必須是原子的,而++和--不具備這個能力。InterLocked類還提供了兩個另外的函數Exchange, CompareExchange用于實現交換和比較交換。Exchange操作會將新值設置到變量中并返回變量的原來值: int oVal = InterLocked.Exchange(ref val, 1)。

監視器(Monitor)

在MSDN中對Monitor的描述是: Monitor 類通過向單個線程授予對象鎖來控制對對象的訪問。

Monitor類是一個靜態類因此你不能通過實例化來得到類的對象。Monitor 的成員可以查看MSDN,基本上Monitor的效果和lock是一樣的,通過加鎖操作Enter設置臨界區,完成操作后使用Exit操作來釋放對象鎖。 不過相對來說Monitor的功能更強,Moniter可以進行測試鎖的狀態,因此你可以控制對臨界區的訪問選擇,等待or離開, 而且Monitor還可以在釋放鎖之前通知指定的對象,更重要的是使用Monitor可以跨越方法來操作。Monitor提供的方法很少就只有獲取鎖的方 法Enter, TryEnter;釋放鎖的方法Wait, Exit;還有消息通知方法Pulse, PulseAll。經典的Monitor操作是這樣的:

// 通監視器來創建臨界區 static public void DelUser(string name){  try  {  // 等待線程進入   Monitor.Enter(Names);  Names.Remove(name);  Console.WriteLine("Del: {0}", Names.Count);  Monitor.Pulse(Names);  }  finally  {  // 釋放對象鎖   Monitor.Exit(Names);  } } }

其中Names是一個List, 這里有一個小技巧,如果你想聲明整個方法為線程同步可以使用方法屬性:

// 通過屬性設置整個方法為臨界區 [MethodImpl(MethodImplOptions.Synchronized)] static public void AddUser(string name) {  Names.Add(name);  Console.WriteLine("Add: {0}",Names.Count); }

對于Monitor的使用有一個方法是比較詭異的,那就是Wait方法。在MSDN中對Wait的描述是: 釋放對象上的鎖以便允許其他線程鎖定和訪問該對象。

這里提到的是先釋放鎖,那么顯然我們需要先得到鎖,否則調用Wait會出現異常,所 以我們必須在Wait前面調用Enter方法或其他獲取鎖的方法,如lock,這點很重要。對應Enter方法,Monitor給出來另一種實現 TryEnter。這兩種方法的主要區別在于是否阻塞當前線程,Enter方法在獲取不到鎖時,會阻塞當前線程直到得到鎖。不過缺點是如果永遠得不到鎖那 么程序就會進入死鎖狀態。我們可以采用Wait來解決,在調用Wait時加入超時時限就可以。

if (Monitor.TryEnter(Names)){ Monitor.Wait(Names, 1000); // !!  Names.Remove(name);  Console.WriteLine("Del: {0}", Names.Count); Monitor.Pulse(Names); }

互斥鎖(lock)

lock關鍵字是實現線程同步的比較簡單的方式,其實就是設置一個臨界區。在 lock之后的{...}區塊為一個臨界區,當進入臨界區時加互斥鎖,離開臨界區時釋放互斥鎖。MSDN對lock關鍵字的描述是: lock 關鍵字可將語句塊標記為臨界區,方法是獲取給定對象的互斥鎖,執行語句,然后釋放該鎖。

具體例子如下:

static public void ThreadFunc(object name){ string str = name as string; Random rand = new Random(); int count = rand.Next(100, 200); for (int i = 0; i < count; i++) { lock (NumList) { NumList.Add(i); Console.WriteLine("{0} {1}", str, i); } }}

對lock的使用有幾點建議:對實例鎖定lock(this),對靜態變量鎖定lock(typeof(val))。lock的對象訪問權限最好是private,否則會出現失去訪問控制現象。

讀寫鎖(ReadWriteLock)

讀寫鎖的出現主要是在很多情況下,我們讀資源的操作要多于寫資源的操作。但是如果每 次只對資源賦予一個線程的訪問權限顯然是低效的,讀寫鎖的優勢是同時可以有多個線程對同一資源進行讀操作。因此在讀操作比寫操作多很多,并且寫操作的時間 很短的情況下使用讀寫鎖是比較有效率的。讀寫鎖是一個非靜態類所以你在使用前需要先聲明一個讀寫鎖對象:

static private ReaderWriterLock _rwlock = new ReaderWriterLock();

讀寫鎖是通過調用AcquireReaderLock,ReleaseReaderLock,AcquireWriterLock,ReleaseWriterLock來完成讀鎖和寫鎖控制的

static public void ReaderThread(int thrdId) {   try   { // 請求讀鎖,如果100ms超時退出   _rwlock.AcquireReaderLock(10);   try   {    int inx = _rand.Next(_list.Count);    if (inx < _list.Count)    Console.WriteLine("{0}thread {1}", thrdId, _list[inx]);   }   finally   {   _rwlock.ReleaseReaderLock();   }   }   catch (ApplicationException) // 如果請求讀鎖失敗   {   Console.WriteLine("{0}thread get reader lock out time!", thrdId);   }  }  static public void WriterThread()  {   try   {  // 請求寫鎖   _rwlock.AcquireWriterLock(100);   try   {    string val = _rand.Next(200).ToString();    _list.Add(val); // 寫入資源    Console.WriteLine("writer thread has written {0}", val);   }   finally   { // 釋放寫鎖    _rwlock.ReleaseWriterLock();   }   }   catch (ApplicationException)   {   Console.WriteLine("Get writer thread lock out time!");   } }

如果你想在讀的時候插入寫操作請使用UpgradeToWriterLock和DowngradeFromWriterLock來進行操作,而不是釋放讀鎖。

static private void UpgradeAndDowngrade(int thrdId) {  try  {  _rwlock.AcquireReaderLock(10);  try  {   try   {  // 提升讀鎖到寫鎖   LockCookie lc = _rwlock.UpgradeToWriterLock(100);  try  {  string val = _rand.Next(500).ToString();  _list.Add(val); Console.WriteLine("Upgrade Thread{0} add {1}", thrdId, val);   }   finally  { // 下降寫鎖   _rwlock.DowngradeFromWriterLock(ref lc);   }   }   catch (ApplicationException)  {   Console.WriteLine("{0}thread upgrade reader lock failed!", thrdId);   }  }  finally  {  // 釋放原來的讀鎖   _rwlock.ReleaseReaderLock(); } }  catch (ApplicationException)  {  Console.WriteLine("{0}thread get reader lock out time!", thrdId); }}

這里有一點要注意的就是讀鎖和寫鎖的超時等待時間間隔的設置。通常情況下設置寫鎖的等待超時要比讀鎖的長,否則會經常發生寫鎖等待失敗的情況。

系統內核對象 互斥對象(Mutex)

互斥對象的作用有點類似于監視器對象,確保一個代碼塊在同一時刻只有一個線程在執 行?;コ鈱ο蠛捅O視器對象的主要區別就是,互斥對象一般用于跨進程間的線程同步,而監視器對象則用于進程內的線程同步?;コ鈱ο笥袃煞N:一種是命名互斥; 另一種是匿名互斥。在跨進程中使用到的就是命名互斥,一個已命名的互斥就是一個系統級的互斥,它可以被其他進程所使用,只要在創建互斥時指定打開互斥的名 稱就可以。在.Net中互斥是通過Mutex類來實現。

其實對于OpenExisting函數有兩個重載版本,

Mutex.OpenExisting (String)
Mutex.OpenExisting (String, MutexRights)

對于默認的第一個函數其實是實現了第二個函數 MutexRights.Synchronize|MutexRights.Modify操作。

由于監視器的設計是基于.Net框架,而Mutex類是系統內核對象封裝了win32的一個內核結構來實現互斥,并且互斥操作需要請求中斷來完成,因此在進行進程內線程同步的時候性能上要比互斥要好。

典型的使用Mutex同步需要完成三個步驟的操作:1.打開或者創建一個Mutex實例;2.調用WaitOne()來請求互斥對象;3.最后調用ReleaseMutex來釋放互斥對象。

static public void AddString(string str) { // 設置超時時限并在wait前退出非默認托管上下文  if (_mtx.WaitOne(1000, true)) { _resource.Add(str);  _mtx.ReleaseMutex();  } }

需要注意的是,WaitOne和ReleaseMutex必須成對出現,否則會導致進程死鎖的發生,這時系統(.Net2.0)框架會拋出AbandonedMutexException異常。

信號量(Semaphore)

信號量就像一個夜總會:它有確切的容量,并被保鏢控制。一旦滿員,就沒有人能再進入,其他人必須在外面排隊。那么在里面離開一個人后,隊頭的人就可以進入。信號量的構造函數需要提供至少兩個參數-現有的人數和最大的人數。

信號量的行為有點類似于Mutex或是lock,但是信號量沒有擁有者。任意線程都可以調用Release來釋放信號量而不像Mutex和lock那樣需要線程得到資源才能釋放。

class SemaphoreTest { static Semaphore s = new Semaphore(3, 3); // 當前值=3; 容量=3  static void Main()  {  for (int i = 0; i < 10; i++)   new Thread(Go).Start();  }  static void Go() {  while (true)  {  s.WaitOne();  Thread.Sleep(100); // 一次只有個線程能被處理   s.Release();   }  }} 事件(ManualResetEvent/AutoResetEvent) < src="http://blog.csdn.net/count.aspx?ID=1857459&Type=Rank" type="text/javascript">

AutoResetEvent

一個AutoResetEvent象是一個"檢票輪盤":插入一張通行證然后讓一個 人通過。"auto"的意思就是這個"輪盤"自動關閉或者打開讓某人通過。線程將在調用WaitOne后進行等待或者是阻塞,并且通過調用Set操作來插 入線程。如果一堆線程調用了WaitOne操作,那么"輪盤"就會建立一個等待隊列。一個通行證可以來自任意一個線程,換句話說任意一個線程都可以通過訪 問AutoResetEvent對象并調用Set來釋放一個阻塞的線程。

如果在Set被調用的時候沒有線程等待,那么句柄就會一直處于打開狀態直到有線程調 用了WaitOne操作。這種行為避免了競爭條件-當一個線程還沒來得急釋放而另一個線程就開始進入的情況。因此重復的調用Set操作一個"輪盤"哪怕是 沒有等待線程也不會一次性的讓所有線程進入。

WaitOne操作接受一個超時參數-當發生等待超時的時候,這個方法會返回一個 false。當已有一個線程在等待的時候,WaitOne操作可以指定等待還是退出當前同步上下文。Reset操作提供了關閉"輪盤"的操作。 AutoResetEvent能夠通過兩個方法來創建: 1.調用構造函數 EventWaitHandle wh = new AutoResetEvent (false); 如果boolean值為true,那么句柄的Set操作將在創建后自動被調用 ;2. 通過基類EventWaitHandle方式 EventWaitHandle wh = new EventWaitHandle (false, EventResetMode.Auto); EventWaitHandle構造函數允許創建一個ManualResetEvent。人們應該通過調用Close來釋放一個Wait Handle在它不再使用的時候。當在應用程序的生存期內Wait handle繼續被使用,那么如果遺漏了Close這步,在應用程序關閉的時候也會被自動釋放。

class BasicWaitHandle { static EventWaitHandle wh = new AutoResetEvent(false); static void Main() {  new Thread(Waiter).Start();  Thread.Sleep(1000); // 等待一會兒   wh.Set(); // 喚醒  }  static void Waiter() {  Console.WriteLine("Waiting...");  wh.WaitOne(); // 等待喚醒   Console.WriteLine("Notified");  }}

ManualResetEvent

ManualResetEvent是AutoResetEvent的一個特例。它的 不同之處在于在線程調用WaitOne后不會自動的重置狀態。它的工作機制有點象是開關:調用Set打開并允許其他線程進行WaitOne;調用 Reset關閉那么排隊的線程就要等待,直到下一次打開??梢允褂靡粋€帶volatile聲明的boolean字段來模擬間斷休眠 - 通過重復檢測標志,然后休眠一小段時間。

ManualResetEvent常常被用于協助完成一個特殊的操作,或者讓一個線程在開始工作前完成初始化。

線程池(Thread Pooling)

如果你的應用程序擁有大量的線程并花費大量的時間阻塞在一個Wait Handle上,那么你要考慮使用線程池(Thead pooling)來處理。線程池通過合并多個Wait Handle來節約等待的時間。當Wait Handle被激活時,使用線程池你需要注冊一個Wait Handle到一個委托去執行。通過調用ThreadPool.RegisterWaitForSingleObject方法:

class Test { static ManualResetEvent starter = new ManualResetEvent(false);  public static void Main() {  ThreadPool.RegisterWaitForSingleObject(starter,Go,"hello",-1,true);  Thread.Sleep(5000);  Console.WriteLine("Signaling worker...");  starter.Set();   Console.ReadLine(); } public static void Go(object data, bool timedOut)  {  Console.WriteLine("Started " + data); // Perform task...  }}

對于Wait Handle和委托,RegisterWaitForSingleObject接受一個"黑盒"對象并傳遞給你的委托(就像 ParameterizedThreadStart),超時設置和boolean標志指示了關閉和循環的請求。所有進入池中的線程都被認為是后臺線程,這 就意味著它們不再由應用程序控制,而是由系統控制直到應用程序退出。

注意:如果這時候調用Abort操作,可能會發生意想不到的情況。

你也可以通過調用QueueUserWorkItem方法使用線程池,指定委托并立即被執行。這時你不能在多任務情況下保存共享線程,但是可以得到另外的好處:線程池會保持一個線程的總容量,當作業數超出容量時自動插入任務。

class Test { static object workerLocker = new object(); static int runningWorkers = 100; public static void Main()  {  for (int i = 0; i < runningWorkers; i++)   {  ThreadPool.QueueUserWorkItem(Go, i);   }  Console.WriteLine("Waiting for threads to complete...");   lock (workerLocker)   {  while (runningWorkers > 0)    Monitor.Wait(workerLocker);  }  Console.WriteLine("Complete!");  Console.ReadLine();  } public static void Go(object instance)  {  Console.WriteLine("Started: " + instance);  Thread.Sleep(1000);   Console.WriteLine("Ended: " + instance);   lock (workerLocker)  {  runningWorkers--;  Monitor.Pulse(workerLocker);  } }}

為了傳遞多個對象到目標方法,你必須定義一個客戶對象并包含所有屬性或通過調用異步的委托。如Go方法接受兩參數:

ThreadPool.QueueUserWorkItem (delegate (object notUsed) { Go (23,34); });

其他的方法可以使用異步委托。

希望本文所述對大家的C#程序設計有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
理论片在线不卡免费观看| 大荫蒂欧美视频另类xxxx| 欧美视频在线观看 亚洲欧| 日韩欧美一区二区三区| 精品小视频在线| 91精品国产高清自在线看超| 亚洲福利视频免费观看| 欧美精品福利视频| 日韩一区二区久久久| 亚洲欧美制服综合另类| 欧美日韩精品中文字幕| 久久久久久久久久久免费精品| 欧美成人午夜影院| 国产91网红主播在线观看| 91精品视频网站| 国产精品高潮在线| 国产一区二区丝袜高跟鞋图片| 亚洲视频综合网| 欧美一级淫片播放口| 欧美中文在线观看国产| 俺也去精品视频在线观看| 国产成人亚洲精品| 亚洲高清久久久久久| 麻豆精品精华液| 亚洲人成在线播放| 最近2019中文字幕大全第二页| 日韩中文字幕第一页| 日韩不卡中文字幕| 久热精品视频在线免费观看| 日韩av影视综合网| 欧美日韩激情小视频| 2021国产精品视频| 久久久久久久久久久亚洲| 成人欧美一区二区三区黑人孕妇| 中文字幕精品在线| 国产精品电影网站| 成人精品久久一区二区三区| 2019日本中文字幕| 亚洲视频欧美视频| 亚洲人成电影网站色…| 2020欧美日韩在线视频| 欧美第一黄色网| 欧美成人免费全部观看天天性色| 日韩欧美亚洲一二三区| 欧美激情一区二区久久久| 欧亚精品在线观看| 在线精品高清中文字幕| 欧美激情亚洲激情| 欧美性生交xxxxxdddd| 亚洲人成网站在线播| 欧美做爰性生交视频| 91沈先生作品| 亚洲国产精品va在线看黑人动漫| 色综合天天综合网国产成人网| 国内精品中文字幕| 久久网福利资源网站| 中文字幕亚洲字幕| 欧美在线激情网| 日韩电影中文字幕在线| 日韩**中文字幕毛片| 日韩成人在线电影网| 欧美疯狂做受xxxx高潮| 成人黄色av播放免费| 国产精品入口免费视| 97avcom| 亚洲成人国产精品| 精品亚洲男同gayvideo网站| 亚洲天堂第二页| 欧美成人自拍视频| 日韩在线观看免费全集电视剧网站| 超碰97人人做人人爱少妇| 一区二区三区四区精品| 国产精品成人v| 亚洲一区二区三区在线免费观看| 国产一区红桃视频| 亚洲精品国产精品自产a区红杏吧| 国产精品精品久久久| 欧美多人乱p欧美4p久久| 国产精品视频公开费视频| 欧美黑人xxxⅹ高潮交| 亚洲iv一区二区三区| 91欧美精品午夜性色福利在线| 精品久久久久久中文字幕大豆网| 国产欧美一区二区白浆黑人| 日韩精品视频免费| 亚洲va久久久噜噜噜| 国产在线999| 日韩视频中文字幕| 亚洲视频日韩精品| 中文字幕亚洲欧美日韩2019| 亚洲第一偷拍网| 亚洲九九九在线观看| 国产一区二区色| 亚洲欧美国产日韩天堂区| 日韩高清中文字幕| 欧美日韩成人在线观看| 欧美国产视频一区二区| 91成人福利在线| 国产精品视频成人| 97免费视频在线| 国产精品久久久久久影视| 中文字幕久久久| 日韩综合中文字幕| 久久精品成人欧美大片| 国产成人亚洲综合91精品| 欧美色道久久88综合亚洲精品| 清纯唯美日韩制服另类| 成人在线视频网| 欧美视频中文字幕在线| 国产精品美女主播| 欧美日韩国产丝袜另类| 永久555www成人免费| 亚洲欧美中文在线视频| 国外成人在线直播| 奇米四色中文综合久久| 欧美激情在线视频二区| 久久久亚洲国产天美传媒修理工| 亚洲色图13p| 亚洲桃花岛网站| 亚洲第一页中文字幕| 国产成人综合亚洲| 欧美另类暴力丝袜| 91高潮在线观看| 国产日韩视频在线观看| 国产精品视频一区二区高潮| 久久精品免费播放| 国产婷婷成人久久av免费高清| 黄色一区二区在线观看| 亚洲风情亚aⅴ在线发布| 不卡中文字幕av| 欧美野外wwwxxx| 91国自产精品中文字幕亚洲| 911国产网站尤物在线观看| 最好看的2019的中文字幕视频| 亚洲国产精品系列| 97av在线视频免费播放| 欧美日韩免费网站| 成人黄色av免费在线观看| 一区二区三区无码高清视频| 中文字幕日韩av电影| 国产欧美一区二区白浆黑人| 成人性生交xxxxx网站| 亚洲视频999| 亚洲午夜久久久久久久| 国产精品日韩在线播放| 亚洲一区二区免费在线| 欧美日本在线视频中文字字幕| 国产精品久久激情| 日韩大陆欧美高清视频区| 成人福利网站在线观看11| 国产一区二区三区在线视频| 久久久综合免费视频| 欧美中文在线观看国产| 欧美成人精品h版在线观看| 欧美日韩国产色视频| 国产精品久久色| 国产欧美日韩精品丝袜高跟鞋| 91超碰中文字幕久久精品| 国产成人极品视频| 日韩av免费一区| 高清一区二区三区日本久| 国产精品欧美激情在线播放| 精品国产一区二区三区久久狼黑人| 欧美大片va欧美在线播放|