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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > C# >内容正文

C#

C# 多线程及同步简介示例

發(fā)布時(shí)間:2023/12/13 C# 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C# 多线程及同步简介示例 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
60年代,在OS中能擁有資源和獨(dú)立運(yùn)行的基本單位是進(jìn)程,然而隨著計(jì)算機(jī)技術(shù)的發(fā)展,進(jìn)程出現(xiàn)了很多弊端,一是由于進(jìn)程是資源擁有者,創(chuàng)建、撤消與切換存在較大的時(shí)空開銷,因此需要引入輕型進(jìn)程;二是由于對稱多處理機(jī)(SMP)出現(xiàn),可以滿足多個(gè)運(yùn)行單位,而多個(gè)進(jìn)程并行開銷過大。 因此在80年代,出現(xiàn)了能獨(dú)立運(yùn)行的基本單位——線程(Threads)。 線程,有時(shí)被稱為輕量級進(jìn)程(Lightweight Process,LWP),是程序執(zhí)行流的最小單元。一個(gè)標(biāo)準(zhǔn)的線程由線程ID,當(dāng)前指令指針(PC),寄存器集合和堆棧組成。另外,線程是進(jìn)程中的一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)兒在運(yùn)行中必不可少的資源,但它可與同屬一個(gè)進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源。一個(gè)線程可以創(chuàng)建和撤消另一個(gè)線程,同一進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行。由于線程之間的相互制約,致使線程在運(yùn)行中呈現(xiàn)出間斷性。線程也有就緒、阻塞和運(yùn)行三種基本狀態(tài)。就緒狀態(tài)是指線程具備運(yùn)行的所有條件,邏輯上可以運(yùn)行,在等待處理機(jī);運(yùn)行狀態(tài)是指線程占有處理機(jī)正在運(yùn)行;阻塞狀態(tài)是指線程在等待一個(gè)事件(如某個(gè)信號量),邏輯上不可執(zhí)行。每一個(gè)程序都至少有一個(gè)線程,若程序只有一個(gè)線程,那就是程序本身。 線程是程序中一個(gè)單一的順序控制流程。進(jìn)程內(nèi)一個(gè)相對獨(dú)立的、可調(diào)度的執(zhí)行單元,是系統(tǒng)獨(dú)立調(diào)度和分派CPU的基本單位指運(yùn)行中的程序的調(diào)度單位。在單個(gè)程序中同時(shí)運(yùn)行多個(gè)線程完成不同的工作,稱為多線程。

?

一、線程簡義

1、進(jìn)程與線程:進(jìn)程作為操作系統(tǒng)執(zhí)行程序的基本單位,擁有應(yīng)用程序的資源,進(jìn)程包含線程,進(jìn)程的資源被線程共享,線程不擁有資源。

2、前臺(tái)線程和后臺(tái)線程:通過Thread類新建線程默認(rèn)為前臺(tái)線程。當(dāng)所有前臺(tái)線程關(guān)閉時(shí),所有的后臺(tái)線程也會(huì)被直接終止,不會(huì)拋出異常。

3、掛起(Suspend)和喚醒(Resume):由于線程的執(zhí)行順序和程序的執(zhí)行情況不可預(yù)知,所以使用掛起和喚醒容易發(fā)生死鎖的情況,在實(shí)際應(yīng)用中應(yīng)該盡量少用。

4、阻塞線程:Join,阻塞調(diào)用線程,直到該線程終止。

5、終止線程:Abort:拋出 ThreadAbortException 異常讓線程終止,終止后的線程不可喚醒。Interrupt:拋出 ThreadInterruptException 異常讓線程終止,通過捕獲異??梢岳^續(xù)執(zhí)行。

6、線程優(yōu)先級:AboveNormal BelowNormal Highest Lowest Normal,默認(rèn)為Normal。

二、線程的使用

線程函數(shù)通過委托傳遞,可以不帶參數(shù),也可以帶參數(shù)(只能有一個(gè)參數(shù)),可以用一個(gè)類或結(jié)構(gòu)體封裝參數(shù)。

1 namespace Test 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Thread t1 = new Thread(new ThreadStart(TestMethod)); 8 Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod)); 9 t1.IsBackground = true; 10 t2.IsBackground = true; 11 t1.Start(); 12 t2.Start("hello"); 13 Console.ReadKey(); 14 } 15 16 public static void TestMethod() 17 { 18 Console.WriteLine("不帶參數(shù)的線程函數(shù)"); 19 } 20 21 public static void TestMethod(object data) 22 { 23 string datastr = data as string; 24 Console.WriteLine("帶參數(shù)的線程函數(shù),參數(shù)為:{0}", datastr); 25 } 26 } 27 }

?

三、線程池

由于線程的創(chuàng)建和銷毀需要耗費(fèi)一定的開銷,過多的使用線程會(huì)造成內(nèi)存資源的浪費(fèi),出于對性能的考慮,于是引入了線程池的概念。線程池維護(hù)一個(gè)請求隊(duì)列,線程池的代碼從隊(duì)列提取任務(wù),然后委派給線程池的一個(gè)線程執(zhí)行,線程執(zhí)行完不會(huì)被立即銷毀,這樣既可以在后臺(tái)執(zhí)行任務(wù),又可以減少線程創(chuàng)建和銷毀所帶來的開銷。

線程池線程默認(rèn)為后臺(tái)線程(IsBackground)。

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //將工作項(xiàng)加入到線程池隊(duì)列中,這里可以傳遞一個(gè)線程參數(shù) 6 ThreadPool.QueueUserWorkItem(TestMethod, "Hello"); 7 Console.ReadKey(); 8 } 9 10 public static void TestMethod(object data) 11 { 12 string datastr = data as string; 13 Console.WriteLine(datastr); 14 } 15 }

?

四、Task類

使用ThreadPool的QueueUserWorkItem()方法發(fā)起一次異步的線程執(zhí)行很簡單,但是該方法最大的問題是沒有一個(gè)內(nèi)建的機(jī)制讓你知道操作什么時(shí)候完成,有沒有一個(gè)內(nèi)建的機(jī)制在操作完成后獲得一個(gè)返回值。為此,可以使用System.Threading.Tasks中的Task類。

構(gòu)造一個(gè)Task<TResult>對象,并為泛型TResult參數(shù)傳遞一個(gè)操作的返回類型。

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000); 6 t.Start(); 7 t.Wait(); 8 Console.WriteLine(t.Result); 9 Console.ReadKey(); 10 } 11 12 private static Int32 Sum(Int32 n) 13 { 14 Int32 sum = 0; 15 for (; n > 0; --n) 16 checked{ sum += n;} //結(jié)果太大,拋出異常 17 return sum; 18 } 19 }

?

一個(gè)任務(wù)完成時(shí),自動(dòng)啟動(dòng)一個(gè)新任務(wù)。
一個(gè)任務(wù)完成后,它可以啟動(dòng)另一個(gè)任務(wù),下面重寫了前面的代碼,不阻塞任何線程。

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000); 6 t.Start(); 7 //t.Wait(); 8 Task cwt = t.ContinueWith(task => Console.WriteLine("The result is {0}",t.Result)); 9 Console.ReadKey(); 10 } 11 12 private static Int32 Sum(Int32 n) 13 { 14 Int32 sum = 0; 15 for (; n > 0; --n) 16 checked{ sum += n;} //結(jié)果溢出,拋出異常 17 return sum; 18 } 19 }

?

五、委托異步執(zhí)行

委托的異步調(diào)用:BeginInvoke() 和 EndInvoke()

1 public delegate string MyDelegate(object data); 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 MyDelegate mydelegate = new MyDelegate(TestMethod); 7 IAsyncResult result = mydelegate.BeginInvoke("Thread Param", TestCallback, "Callback Param"); 8 9 //異步執(zhí)行完成 10 string resultstr = mydelegate.EndInvoke(result); 11 } 12 13 //線程函數(shù) 14 public static string TestMethod(object data) 15 { 16 string datastr = data as string; 17 return datastr; 18 } 19 20 //異步回調(diào)函數(shù) 21 public static void TestCallback(IAsyncResult data) 22 { 23 Console.WriteLine(data.AsyncState); 24 } 25 }

?

六、線程同步

  1)原子操作(Interlocked):幫助保護(hù)免受計(jì)劃程序切換上下文時(shí)某個(gè)線程正在更新可以由其他線程訪問的變量或者在單獨(dú)的處理器上同時(shí)執(zhí)行兩個(gè)線程就可能出現(xiàn)的錯(cuò)誤。 此類的成員不會(huì)引發(fā)異常。

1 class Program 2 { 3 static int counter = 1; 4 5 static void Main(string[] args) 6 { 7 Thread t1 = new Thread(new ThreadStart(F1)); 8 Thread t2 = new Thread(new ThreadStart(F2)); 9 10 t1.Start(); 11 t2.Start(); 12 13 t1.Join(); 14 t2.Join(); 15 16 System.Console.ReadKey(); 17 } 18 19 static void F1() 20 { 21 for (int i = 0; i < 5; i++) 22 { 23 Interlocked.Increment(ref counter); 24 System.Console.WriteLine("Counter++ {0}", counter); 25 Thread.Sleep(10); 26 } 27 } 28 29 static void F2() 30 { 31 for (int i = 0; i < 5; i++) 32 { 33 Interlocked.Decrement(ref counter); 34 System.Console.WriteLine("Counter-- {0}", counter); 35 Thread.Sleep(10); 36 } 37 } 38 }

?

  2)lock()語句:避免鎖定public類型,否則實(shí)例將超出代碼控制的范圍,定義private對象來鎖定。而自定義類推薦用私有的只讀靜態(tài)對象,比如:private static readonly object obj = new object();為什么要設(shè)置成只讀的呢?這時(shí)因?yàn)槿绻趌ock代碼段中改變obj的值,其它線程就暢通無阻了,因?yàn)榛コ怄i的對象變了,object.ReferenceEquals必然返回false。Array 類型提供 SyncRoot。許多集合類型也提供 SyncRoot。

  3)Monitor實(shí)現(xiàn)線程同步

    通過Monitor.Enter() 和 Monitor.Exit()實(shí)現(xiàn)排它鎖的獲取和釋放,獲取之后獨(dú)占資源,不允許其他線程訪問。

    還有一個(gè)TryEnter方法,請求不到資源時(shí)不會(huì)阻塞等待,可以設(shè)置超時(shí)時(shí)間,獲取不到直接返回false。

1 public void MonitorSomeThing() 2 { 3 try 4 { 5 Monitor.Enter(obj); 6 dosomething(); 7 } 8 catch(Exception ex) 9 { 10 11 } 12 finally 13 { 14 Monitor.Exit(obj); 15 } 16 }

?

  4)ReaderWriterLock

    當(dāng)對資源操作讀多寫少的時(shí)候,為了提高資源的利用率,讓讀操作鎖為共享鎖,多個(gè)線程可以并發(fā)讀取資源,而寫操作為獨(dú)占鎖,只允許一個(gè)線程操作。

1 class SynchronizedCache 2 { 3 private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(); 4 private Dictionary<int, string> innerCache = new Dictionary<int, string>(); 5 6 public string Read(int key) 7 { 8 cacheLock.EnterReadLock(); 9 try 10 { 11 return innerCache[key]; 12 } 13 finally 14 { 15 cacheLock.ExitReaderLock(); 16 } 17 } 18 19 public void Add(int key, string value) 20 { 21 cacheLock.EnterWriteLock(); 22 try 23 { 24 innerCache.Add(key, value); 25 } 26 finally 27 { 28 cacheLock.ExitWriteLock(); 29 } 30 } 31 32 public bool AddWithTimeout(int key, string value, int timeout) 33 { 34 if (cacheLock.TryEnterWriteLock(timeout)) 35 { 36 try 37 { 38 innerCache.Add(key, value); 39 } 40 finally 41 { 42 cacheLock.ExitReaderLock(); 43 } 44 return true; 45 } 46 else 47 { 48 return false; 49 } 50 } 51 52 public AddOrUpdateStatus AddOrUpdate(int key, string value) 53 { 54 cacheLock.EnterUpgradeableReadLock(); 55 try 56 { 57 string result = null; 58 if (innerCache.TryGetValue(key, out result)) 59 { 60 if (result == value) 61 { 62 return AddOrUpdateStatus.Unchanged; 63 } 64 else 65 { 66 cacheLock.EnterWriteLock(); 67 try 68 { 69 innerCache[key] = value; 70 } 71 finally 72 { 73 cacheLock.ExitWriteLock(); 74 } 75 return AddOrUpdateStatus.Updated; 76 } 77 } 78 else 79 { 80 cacheLock.EnterWriteLock(); 81 try 82 { 83 innerCache.Add(key, value); 84 } 85 finally 86 { 87 cacheLock.ExitWriteLock(); 88 } 89 return AddOrUpdateStatus.Added; 90 } 91 } 92 finally 93 { 94 cacheLock.ExitUpgradeableReadLock(); 95 } 96 } 97 98 public void Delete(int key) 99 { 100 cacheLock.EnterWriteLock(); 101 try 102 { 103 innerCache.Remove(key); 104 } 105 finally 106 { 107 cacheLock.ExitWriteLock(); 108 } 109 } 110 111 public enum AddOrUpdateStatus 112 { 113 Added, 114 Updated, 115 Unchanged 116 }; 117 }

?

  5)事件(Event)類實(shí)現(xiàn)同步

    事件類有兩種狀態(tài),終止?fàn)顟B(tài)和非終止?fàn)顟B(tài),終止?fàn)顟B(tài)時(shí)調(diào)用WaitOne可以請求成功,通過Set將時(shí)間狀態(tài)設(shè)置為終止?fàn)顟B(tài)。

    1)AutoResetEvent(自動(dòng)重置事件)

    2)ManualResetEvent(手動(dòng)重置事件)

? ? ? ? ? ? ??AutoResetEvent和ManualResetEvent這兩個(gè)類經(jīng)常用到, 他們的用法很類似,但也有區(qū)別。Set方法將信號置為發(fā)送狀態(tài),Reset方法將信號置為不發(fā)送狀態(tài),WaitOne等待信號的發(fā)送。可以通過構(gòu)造函數(shù)的參數(shù)值來決定其初始狀態(tài),若為true則非阻塞狀態(tài),為false為阻塞狀態(tài)。如果某個(gè)線程調(diào)用WaitOne方法,則當(dāng)信號處于發(fā)送狀態(tài)時(shí),該線程會(huì)得到信號, 繼續(xù)向下執(zhí)行。其區(qū)別就在調(diào)用后,AutoResetEvent.WaitOne()每次只允許一個(gè)線程進(jìn)入,當(dāng)某個(gè)線程得到信號后,AutoResetEvent會(huì)自動(dòng)又將信號置為不發(fā)送狀態(tài),則其他調(diào)用WaitOne的線程只有繼續(xù)等待.也就是說,AutoResetEvent一次只喚醒一個(gè)線程;而ManualResetEvent則可以喚醒多個(gè)線程,因?yàn)楫?dāng)某個(gè)線程調(diào)用了ManualResetEvent.Set()方法后,其他調(diào)用WaitOne的線程獲得信號得以繼續(xù)執(zhí)行,而ManualResetEvent不會(huì)自動(dòng)將信號置為不發(fā)送。也就是說,除非手工調(diào)用了ManualResetEvent.Reset()方法,則ManualResetEvent將一直保持有信號狀態(tài),ManualResetEvent也就可以同時(shí)喚醒多個(gè)線程繼續(xù)執(zhí)行。

  6)信號量(Semaphore)

      信號量是由內(nèi)核對象維護(hù)的int變量,為0時(shí),線程阻塞,大于0時(shí)解除阻塞,當(dāng)一個(gè)信號量上的等待線程解除阻塞后,信號量計(jì)數(shù)+1。

      線程通過WaitOne將信號量減1,通過Release將信號量加1,使用很簡單。

1 public Thread thrd; 2 //創(chuàng)建一個(gè)可授權(quán)2個(gè)許可證的信號量,且初始值為2 3 static Semaphore sem = new Semaphore(2, 2); 4 5 public mythread(string name) 6 { 7 thrd = new Thread(this.run); 8 thrd.Name = name; 9 thrd.Start(); 10 } 11 void run() 12 { 13 Console.WriteLine(thrd.Name + "正在等待一個(gè)許可證……"); 14 //申請一個(gè)許可證 15 sem.WaitOne(); 16 Console.WriteLine(thrd.Name + "申請到許可證……"); 17 for (int i = 0; i < 4 ; i++) 18 { 19 Console.WriteLine(thrd.Name + "" + i); 20 Thread.Sleep(1000); 21 } 22 Console.WriteLine(thrd.Name + " 釋放許可證……"); 23 //釋放 24 sem.Release(); 25 } 26 } 27 28 class mysemaphore 29 { 30 public static void Main() 31 { 32 mythread mythrd1 = new mythread("Thrd #1"); 33 mythread mythrd2 = new mythread("Thrd #2"); 34 mythread mythrd3 = new mythread("Thrd #3"); 35 mythread mythrd4 = new mythread("Thrd #4"); 36 mythrd1.thrd.Join(); 37 mythrd2.thrd.Join(); 38 mythrd3.thrd.Join(); 39 mythrd4.thrd.Join(); 40 } 41 }

?

  7)互斥體(Mutex)

      獨(dú)占資源,可以把Mutex看作一個(gè)出租車,乘客看作線程。乘客首先等車,然后上車,最后下車。當(dāng)一個(gè)乘客在車上時(shí),其他乘客就只有等他下車以后才可以上車。而線程與C# Mutex對象的關(guān)系也正是如此,線程使用Mutex.WaitOne()方法等待C# Mutex對象被釋放,如果它等待的C# Mutex對象被釋放了,它就自動(dòng)擁有這個(gè)對象,直到它調(diào)用Mutex.ReleaseMutex()方法釋放這個(gè)對象,而在此期間,其他想要獲取這個(gè)C# Mutex對象的線程都只有等待。

1 class Test 2 { 3 /// <summary> 4 /// 應(yīng)用程序的主入口點(diǎn)。 5 /// </summary> 6 [STAThread] 7 static void Main(string[] args) 8 { 9 bool flag = false; 10 System.Threading.Mutex mutex = new System.Threading.Mutex(true, "Test", out flag); 11 //第一個(gè)參數(shù):true--給調(diào)用線程賦予互斥體的初始所屬權(quán) 12 //第一個(gè)參數(shù):互斥體的名稱 13 //第三個(gè)參數(shù):返回值,如果調(diào)用線程已被授予互斥體的初始所屬權(quán),則返回true 14 if (flag) 15 { 16 Console.Write("Running"); 17 } 18 else 19 { 20 Console.Write("Another is Running"); 21 System.Threading.Thread.Sleep(5000);//線程掛起5秒鐘 22 Environment.Exit(1);//退出程序 23 } 24 Console.ReadLine(); 25 } 26 }

?

?  8)跨進(jìn)程間的同步

      通過設(shè)置同步對象的名稱就可以實(shí)現(xiàn)系統(tǒng)級的同步,不同應(yīng)用程序通過同步對象的名稱識(shí)別不同同步對象。

1 static void Main(string[] args) 2 { 3 string MutexName = "InterProcessSyncName"; 4 Mutex SyncNamed; //聲明一個(gè)已命名的互斥對象 5 try 6 { 7 SyncNamed = Mutex.OpenExisting(MutexName); //如果此命名互斥對象已存在則請求打開 8 } 9 catch (WaitHandleCannotBeOpenedException) 10 { 11 SyncNamed = new Mutex(false, MutexName); //如果初次運(yùn)行沒有已命名的互斥對象則創(chuàng)建一個(gè) 12 } 13 Task MulTesk = new Task 14 ( 15 () => //多任務(wù)并行計(jì)算中的匿名方法,用委托也可以 16 { 17 for (; ; ) //為了效果明顯而設(shè)計(jì) 18 { 19 Console.WriteLine("當(dāng)前進(jìn)程等待獲取互斥訪問權(quán)......"); 20 SyncNamed.WaitOne(); 21 Console.WriteLine("獲取互斥訪問權(quán),訪問資源完畢,按回車釋放互斥資料訪問權(quán)."); 22 Console.ReadLine(); 23 SyncNamed.ReleaseMutex(); 24 Console.WriteLine("已釋放互斥訪問權(quán)。"); 25 } 26 } 27 ); 28 MulTesk.Start(); 29 MulTesk.Wait(); 30 }

?  9)分布式的同步

  可以使用redis任務(wù)隊(duì)列或者redis相關(guān)特性

1 Parallel.For(0, 1000000, i => 2 { 3 Stopwatch sw1 = new Stopwatch(); 4 sw1.Start(); 5 6 if (redisHelper.GetRedisOperation().Lock(key)) 7 { 8 var tt = int.Parse(redisHelper.GetRedisOperation().StringGet("calc")); 9 10 tt++; 11 12 redisHelper.GetRedisOperation().StringSet("calc", tt.ToString()); 13 14 redisHelper.GetRedisOperation().UnLock(key); 15 } 16 var v = sw1.ElapsedMilliseconds; 17 if (v >= 10 * 1000) 18 { 19 Console.Write("f"); 20 } 21 sw1.Stop(); 22 });

?

?

?


轉(zhuǎn)載請標(biāo)明本文來源:http://www.cnblogs.com/yswenli/p/7421475.html?
更多內(nèi)容歡迎star作者的github:https://github.com/yswenli/
如果發(fā)現(xiàn)本文有什么問題和任何建議,也隨時(shí)歡迎交流~

?

轉(zhuǎn)載于:https://www.cnblogs.com/yswenli/p/7421475.html

總結(jié)

以上是生活随笔為你收集整理的C# 多线程及同步简介示例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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