二、串行(同步):
1.lock、Monitor--注意鎖定的對象必需是引用類型(string類型除外)
示例:
private static object syncObject = new object(); private static void TaskWork(object i) { Console.WriteLine("我是任務:{0}",i); lock (syncObject) { Thread.Sleep(1000); Console.WriteLine("我是任務:{0},線程ID:{1}",i,Thread.CurrentThread.ManagedThreadId); } try { Monitor.Enter(syncObject); Console.WriteLine("我是任務:{0},線程ID:{1}", i, Thread.CurrentThread.ManagedThreadId); } finally { Monitor.Exit(syncObject); } } //調用 Task.Factory.StartNew(TaskWork,1); Task.Factory.StartNew(TaskWork, 2);
2.Interlocked
示例:
int i=1; Interlocked.Increment(ref i); //增量+1=2; Console.WriteLine("i當前的值:{0}", i); Interlocked.Decrement(ref i); //減量-1=0; Console.WriteLine("i當前的值:{0}", i); Interlocked.Exchange(ref i, 2);//賦值=2; Console.WriteLine("i當前的值:{0}",i); Interlocked.CompareExchange(ref i, 10, 2);//比較交換值,當i=2時,則將i賦值為10; Console.WriteLine("i當前的值:{0}", i);
3.Mutex--可以實現進程間的同步,甚至是兩個遠程進程間的同步
示例:
var t1 = new Task(() => { Console.WriteLine("我是第一個任務!"); Mutex m = new Mutex(false, "test"); m.WaitOne(); Console.WriteLine("第一個任務完成!"); m.ReleaseMutex(); }); var t2 = new Task(() => { Console.WriteLine("我是第二個任務!"); Mutex m = new Mutex(false, "test"); m.WaitOne(); Console.WriteLine("第二個任務完成!"); m.ReleaseMutex(); }); t1.Start(); t2.Start();
4.ReaderWriterLock 、ReaderWriterLockSlim--如果在某一時刻資源並沒有獲取寫的獨占權,那麼可以獲得多個讀的訪問權,單個寫入的獨占權,如果某一時刻已經獲取了寫入的獨占權,那麼其它讀取的訪問權必須進行等待.
示例:
static ReaderWriterLock rwLock = new ReaderWriterLock(); static void Read(object state) { Console.WriteLine("我是讀線程,線程ID是:{0}",Thread.CurrentThread.ManagedThreadId); rwLock.AcquireReaderLock(Timeout.Infinite);//無限期等待,需要顯式調用ReleaseReaderLock釋放鎖 var readList = state as IEnumerable<int>; foreach (int item in readList) { Console.WriteLine("讀取當前的值為:{0}", item); Thread.Sleep(500); } Console.WriteLine("讀完成,線程ID是:{0}", Thread.CurrentThread.ManagedThreadId); rwLock.ReleaseReaderLock(); } static void Write(object state) { Console.WriteLine("我是寫線程,線程ID是:{0}", Thread.CurrentThread.ManagedThreadId); rwLock.AcquireWriterLock(Timeout.Infinite); //無限期等待,需要顯式調用ReleaseWriterLock釋放鎖 var writeList = state as List<int>; int lastCount=writeList.Count(); for (int i = lastCount; i <= 10+lastCount; i++) { writeList.Add(i); Console.WriteLine("寫入當前值:{0}",i); Thread.Sleep(500); } Console.WriteLine("寫完成,線程ID是:{0}", Thread.CurrentThread.ManagedThreadId); rwLock.ReleaseWriterLock(); } //調用: var rwList = new List<int>(); var t1 = new Thread(Write); var t2 = new Thread(Read); var t3 = new Thread(Write); var t4 = new Thread(Read); t1.Start(rwList); t2.Start(rwList); t3.Start(rwList); t4.Start(rwList);
5.SynchronizationAttribute--確保某個類的實例在同一時刻只能被一個線程訪問,類的定義要求:A.類上必需標記SynchronizationAttribute特性,B.類必需繼承自System.ContextBoundObject對象
示例:
[Synchronization(SynchronizationAttribute.REQUIRED,true)] public class Account : System.ContextBoundObject { private static int _balance; public int Blance { get { return _balance; } } public Account() { _balance = 1000; } public void WithDraw(string name,object money) { if ((int)money <= _balance) { Thread.Sleep(2000); _balance = _balance - (int)money; Console.WriteLine("{0} 取錢成功!余額={1}", name, _balance); } else { Console.WriteLine("{0} 取錢失敗!余額不足!", name); } } } //調用: var account = new Account(); Parallel.Invoke(() => { account.WithDraw("張三",600); }, () => { account.WithDraw("李四",600); });
6.MethodImplAttribute--使整個方法上鎖,直到方法返回,才釋放鎖
示例:
public class Account { private static int _balance; public int Blance { get { return _balance; } } public Account() { _balance = 1000; } [MethodImpl(MethodImplOptions.Synchronized)] public void WithDraw(string name,object money) { if ((int)money <= _balance) { Thread.Sleep(2000); _balance = _balance - (int)money; Console.WriteLine("{0} 取錢成功!余額={1}", name, _balance); } else { Console.WriteLine("{0} 取錢失敗!余額不足!", name); } } } //調用 var account = new Account(); Parallel.Invoke(() => { account.WithDraw("張三",600); }, () => { account.WithDraw("李四",600); });
7.AutoResetEvent、ManualResetEvent、ManualResetEventSlim--調用WaitOne、WaitAny或WaitAll來使線程等待事件,調用Set方法發送信號,事件將變為終止狀態,等待的線程被喚醒
示例:
AutoResetEvent arEvent = new AutoResetEvent(false);//默認為無信號,處於非終止狀態 Task.Factory.StartNew((o) => { for (int i = 1; i <= 10; i++) { Console.WriteLine("循環第{0}次",i); } arEvent.Set();//發送信號,處於終止狀態 },arEvent); arEvent.WaitOne();//等待信號,收到信號後則繼續下面的執行 Console.WriteLine("我是主線程,我繼續執行!"); Console.Read();
8.Sempaphore、SemaphoreSlim(不可跨進程)--信號量,可實現線程、進程間同步
示例:
public class WashRoom { private readonly Semaphore sem; public WashRoom(int maxUseableCount) { sem = new Semaphore(maxUseableCount, maxUseableCount, "WC"); } public void Use(int i) { Task.Factory.StartNew(() => { Console.WriteLine("第{0}個人等待進入", i); // WaitOne:如果還有“空位”,則占位,如果沒有空位,則等待; sem.WaitOne(); Console.WriteLine("第{0}個人成功進入,使用中", i); // 模擬線程執行了一些操作 Thread.Sleep(100); Console.WriteLine("第{0}個人用完,離開了", i); // Release:釋放一個“空位” sem.Release(); }); } } //調用: var wc = new WashRoom(5); for (int i = 1; i <= 7; i++) { wc.Use(i); }
9.Barrier--屏障,使多個任務能夠采用並行方式依據某種算法在多個階段中協同工作,即:將一個階段的事情分成多個線程來異步執行,執行完畢後再同時進入下一個階段
示例:
int taskSize = 5; Barrier barrier = new Barrier(taskSize, (b) => { Console.WriteLine(string.Format("{0}當前階段編號:{1}{0}", "-".PadRight(15, '-'), b.CurrentPhaseNumber)); }); var tasks = new Task[taskSize]; for (int i = 0; i < taskSize; i++) { tasks[i] = Task.Factory.StartNew((n) => { Console.WriteLine("Task : #{0} ----> 處理了第一部份數據。", n); barrier.SignalAndWait(); Console.WriteLine("Task : #{0} ----> 處理了第二部份數據。", n); barrier.SignalAndWait(); Console.WriteLine("Task : #{0} ----> 處理了第三部份數據。", n); barrier.SignalAndWait(); }, i); } Task.WaitAll(tasks);
10.SpinLock--自旋鎖,僅限鎖定的時間較短
示例:
SpinLock sLock = new SpinLock(); int num = 0; Action action = () => { bool lockTaken = false; for (int i = 0; i < 10; i++) { lockTaken = false; try { sLock.Enter(ref lockTaken); Console.WriteLine("{0}+1={1} ---線程ID:[{2}]", num, ++num,Thread.CurrentThread.ManagedThreadId); Thread.Sleep(new Random().Next(9)); } finally { //真正獲取之後,才釋放 if (lockTaken) sLock.Exit(); } } }; //多線程調用: Parallel.Invoke(action, action, action); Console.WriteLine("合計:{0}", num);
11.SpinWait--自旋等待,輕量級
Thread.Sleep(1000);//線程等待1S; Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); SpinWait.SpinUntil(() => false, 1000);//自旋等待1S Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); Thread.SpinWait(100000);//指定CPU的循環次數,時間間隔處決於處理器的運行速度,一般不建議使用 Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
12.CountdownEvent--與Sempaphore功能類似,但CountdownEvent支持動態調整信號計數
示例:
static void TimeLimitShopping(int custCount,int times,CountdownEvent countdown) { var customers = Enumerable.Range(1, custCount); foreach (var customer in customers) { int currentCustomer = customer; Task.Factory.StartNew(()=> { SpinWait.SpinUntil(() => false, 1000); Console.WriteLine("第{0}波客戶購買情況:Customer-{1}-已購買.", times, currentCustomer); countdown.Signal(); }); //countdown.AddCount(); } } //調用: var countdown = new CountdownEvent(5); TimeLimitShopping(5, 1, countdown); countdown.Wait(); countdown.Reset(10); TimeLimitShopping(10, 2, countdown); countdown.Wait(); countdown.Reset(20); TimeLimitShopping(20, 3, countdown); countdown.Wait();
最後分享在System.Collections.Concurrent命名空間下的幾個並發集合類:
ConcurrentBag<T>:表示線程安全的無序集合;
ConcurrentDictionary<T>:表示線程安全的多個鍵值對集合;
ConcurrentQueue<T>:表示線程安全的先進先出集合;
ConcurrentStack<T>:表示線程安全的後進先出集合;
線程的幾個狀態(以下圖片來源於這篇文章:http://www.cnblogs.com/edisonchou/p/4848131.html):
參考以下相關文章:
歸納一下:C#線程同步的幾種方法
C#編程總結(三)線程同步