一、什么是线程同步
1:线程同步是 多线程中时通共享资源的常用技术 当我们遇到多个线程使用单一对象所造成的竞争条件,如何顺序正确的取执行他称之为线程同步
2:内核模式,当线程a占用共享对象,使其他线程置于阻塞状态 会造成上下文切换(操作系统的线程调度器。保存等待线程,并切换到另一个线程,依次恢复等待线程)
将会消耗性能
3:用户模式,简单的等待,不降线程切换到阻塞状态, 线程等待会浪费cpu时间,但是节省了上下文切换耗费的cpu时间 ,该模式轻,快,适合短时间的线程
4:混合模式:先尝试用户模式,如果时间长就切换内核模式,以节省cpu资源
二、为什么使用线程同步
1:线程同步可以解决多个线程共享一个对象所造成竞争,死锁等复杂的问题
三、怎么使用线程同步
1:使用线程的原子操作(一个线程只占用一个量子的时间,一次就可以完成)无需其他线程等待当前操作wanc,就避免使用锁,也就避免造成死锁,从而不用阻塞线程就可避免竞争条件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Threading; 3 using static System.Console; 4 5 namespace Chapter2.Recipe1 6 { 7 internal class Program 8 { 9 private static void Main(string[] args) 10 { 11 WriteLine("Incorrect counter"); 12 //反面例子:创建三个线程调用TestCounter方法来执行递增递减操作,线程是不安全的,将会造成竞争条件,运行多次将会得到不是非0的结果 13 var c = new Counter(); 14 var t1 = new Thread(() => TestCounter(c)); 15 var t2 = new Thread(() => TestCounter(c)); 16 var t3 = new Thread(() => TestCounter(c)); 17 t1.Start(); 18 t2.Start(); 19 t3.Start(); 20 t1.Join(); 21 t2.Join(); 22 t3.Join(); 23 24 WriteLine($"Total count: {c.Count}"); 25 26 WriteLine("Correct counter"); 27 //正面例子:使用Interlocked为多个线程共享变量提供原子性操作 28 var c1 = new CounterNoLock(); 29 30 t1 = new Thread(() => TestCounter(c1)); 31 t2 = new Thread(() => TestCounter(c1)); 32 t3 = new Thread(() => TestCounter(c1)); 33 t1.Start(); 34 t2.Start(); 35 t3.Start(); 36 t1.Join(); 37 t2.Join(); 38 t3.Join(); 39 40 WriteLine($"Total count: {c1.Count}"); 41 ReadLine(); 42 } 43 44 static void TestCounter(CounterBase c) 45 { 46 for (int i = 0; i < 100000; i++) 47 { 48 c.Incrementa(); 49 c.Decrement(); 50 } 51 } 52 //反面例子 53 class Counter : CounterBase 54 { 55 private int _count; 56 57 public int Count => _count; 58 59 public override void Incrementa() 60 { 61 _count++; 62 } 63 64 public override void Decrement() 65 { 66 _count--; 67 } 68 } 69 70 class CounterNoLock : CounterBase 71 { 72 private int _count; 73 74 public int Count => _count; 75 76 public override void Incrementa() 77 { 78 Interlocked.Increment(ref _count); 79 } 80 81 public override void Decrement() 82 { 83 Interlocked.Decrement(ref _count); 84 } 85 } 86 87 abstract class CounterBase 88 { 89 public abstract void Incrementa(); 90 91 public abstract void Decrement(); 92 } 93 } 94 }
Mutex类
含义:一种原始的同步方法,只对一个线程授予共享资源的独占访问
用法:可用于不同程序中同步线程,可在大量的使用场景中, 如果当需要多线程写入的的时候,可考虑
使用详细:https://www.cnblogs.com/Tench/p/7710994.html
SemaphoreSlim
含义:限制了同时访问同一个资源线程的数量,
AutoResetEvent
含义:信号类,从一个线程向另一个线程发送通知。某线程通知等待的线程有某件事发生
ManuaIResetEventSlim
含义:信号类,在线程之间传递信号
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System; using System.Threading; using static System.Console; using static System.Threading.Thread; namespace Chapter2.Recipe5 { class Program { //_mainEvent.Set(); 开门 _mainEvent.Reset();关门 //ABC三个人发现了一个规律,某个ATM凌晨的时候 5s会吐一次钱,开心的要死 //凌晨一到, A提前一秒就到了ATM钱,果然,ATM吐钱了,A开心的走了 //B离家远 9秒钟才到,果然10秒钟的时候又吐钱了 //C和b住一起,但是没打到滴滴 于是想只要等五秒就可以得到钱钱 开心的要死 谁知道那个龟孙子 _mainEvent.Reset()设置了 999999秒 static void Main(string[] args) { var t1 = new Thread(() => TravelThroughGates("a", 4)); var t2 = new Thread(() => TravelThroughGates("b", 9)); var t3 = new Thread(() => TravelThroughGates("c", 10)); t1.Start(); t2.Start(); t3.Start(); Sleep(TimeSpan.FromSeconds(5)); WriteLine(" ATM吐钱"); _mainEvent.Set(); _mainEvent.Set(); _mainEvent.Set(); Sleep(TimeSpan.FromSeconds(5)); _mainEvent.Reset(); WriteLine(" ATM关闭"); Sleep(TimeSpan.FromSeconds(10)); WriteLine(" ATM关闭吐钱"); _mainEvent.Set(); Sleep(TimeSpan.FromSeconds(999999)); WriteLine(" ATM关闭"); _mainEvent.Reset(); } static void TravelThroughGates(string threadName, int seconds) { WriteLine($"{threadName} 到了"); Sleep(TimeSpan.FromSeconds(seconds)); WriteLine($"{threadName} 等待吐钱"); _mainEvent.Wait(); WriteLine($"{threadName} 拿到钱"); } static ManualResetEventSlim _mainEvent = new ManualResetEventSlim(false); } }
CountDownEvent
含义:信号类,等待一定数量线程的操作完成,后去执行某些事情
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Threading; 3 using static System.Console; 4 using static System.Threading.Thread; 5 6 namespace Chapter2.Recipe6 7 { 8 class Program 9 {//把PerformOperation当做多线程使用的一个共享资源,一个5s,一个20s,只有当线程一起执行过2次之后,主程序才会继续走下去,没达到次数将会一直等待 10 static void Main(string[] args) 11 { 12 WriteLine("Starting two operations"); 13 var t1 = new Thread(() => PerformOperation("Operation 1 is completed", 5)); 14 var t2 = new Thread(() => PerformOperation("Operation 2 is completed", 20)); 15 t1.Start(); 16 t2.Start(); 17 _countdown.Wait(); 18 WriteLine("Both operations have been completed."); 19 _countdown.Dispose(); 20 } 21 22 static CountdownEvent _countdown = new CountdownEvent(2);//设置次数 23 24 static void PerformOperation(string message, int seconds) 25 { 26 Sleep(TimeSpan.FromSeconds(seconds)); 27 WriteLine(message); 28 _countdown.Signal(); 29 } 30 } 31 }
Barrier
含义:用于组织多个线程及时在某个时刻碰面,提供一个回调函数,每次线程调用了SignalAndWait方法后改回调函数会被执行
用法:多线程迭代中非常有用,可在每个迭代钱执行一些计算
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Threading; 3 using static System.Console; 4 using static System.Threading.Thread; 5 6 namespace Chapter2.Recipe7 7 { 8 class Program 9 { 10 //吉他手和歌手一起演奏, 但是歌手2秒就完成,于是触发SignalAndWait方法, 我唱到了第几次 11 static void Main(string[] args) 12 { 13 var t1 = new Thread(() => PlayMusic("吉他手", "演奏", 10)); 14 var t2 = new Thread(() => PlayMusic("歌手", "唱歌", 2)); 15 16 t1.Start(); 17 t2.Start(); 18 Console.ReadLine(); 19 } 20 21 static Barrier _barrier = new Barrier(1, 22 b => WriteLine($"阶段完成{b.CurrentPhaseNumber + 1}")); 23 24 static void PlayMusic(string name, string message, int seconds) 25 { 26 for (int i = 0; i < 3; i++) 27 { 28 WriteLine(name+"----------------------开始------------------------"); 29 Sleep(TimeSpan.FromSeconds(seconds)); 30 WriteLine($"{name} 开始 to {message}"); 31 Sleep(TimeSpan.FromSeconds(seconds)); 32 WriteLine($"{name} 结束 to {message}"); 33 _barrier.SignalAndWait(); 34 } 35 } 36 } 37 }