多线程-3(同步)
Posted ccaa
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程-3(同步)相关的知识,希望对你有一定的参考价值。
SemaphoreSlim类
代码:
static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(4);//只允许4个并行执行的线程 public static void AccessDataBase(string name,int seconde) { Console.WriteLine("{0} 等待访问数据库", name); semaphoreSlim.Wait(); Console.WriteLine("{0} 授予对数据库的访问权", name); Thread.Sleep(TimeSpan.FromSeconds(seconde)); Console.WriteLine("{0} 完成", name); semaphoreSlim.Release(); } static void Main(string[] args) { for (int i = 0; i < 6; i++) { string threadName = "Thread" + i; int secondsToWait = 2 + 2 * i; var t = new Thread(() => AccessDataBase(threadName, secondsToWait)); t.Start(); } }
主线程启动,创建SemaphoreSlim的一个实例,在构造函数中指定允许并发线程数量,启动6个不同名称和不同初始运行时间的线程,借助信号系统限制访问数据的并发数, 只允许4个线程获取, 注意:.当线程1完成后,线程0才进行授权访问。
AutoResetEven类
private static AutoResetEvent workResetEvent = new AutoResetEvent(false); private static AutoResetEvent mainRestEvent = new AutoResetEvent(false); static void Process(int seconds) { Console.WriteLine("开始一项长期的工作"); Thread.Sleep(TimeSpan.FromSeconds(seconds)); Console.WriteLine("work执行完毕"); workResetEvent.Set(); Console.WriteLine("等待主线程完成它的工作"); mainRestEvent.WaitOne(); Console.WriteLine("开始第二次操作"); Thread.Sleep(TimeSpan.FromSeconds(seconds)); Console.WriteLine("工作完成"); workResetEvent.Set(); } static void Main(string[] args) { var t = new Thread(() => Process(10)); t.Start(); workResetEvent.WaitOne(); Console.WriteLine("第一次操作完成"); Console.WriteLine("在第二个线程上执行操作"); Thread.Sleep(TimeSpan.FromSeconds(5)); mainRestEvent.Set(); Console.WriteLine("现在在第二个线程上运行第二个操作"); workResetEvent.WaitOne(); Console.WriteLine("第二个线程执行完毕"); }
AutoResetEvent类采用的是内核时间模式,所以等待时间不能太长。使用ManualResetEventslim类更好。
ManualResetEventSlim类
static ManualResetEventSlim mainEvent = new ManualResetEventSlim(false); static void TravleThroughGates(string threadName,int seconds) { Console.WriteLine("{0} falls to sleep",threadName); Thread.Sleep(TimeSpan.FromSeconds(seconds)); Console.WriteLine("{0} waits for the gates to open!",threadName); mainEvent.Wait();// Console.WriteLine("{0} enters the gates!", threadName); } static void Main(string[] args) { var t1 = new Thread(() => TravleThroughGates("threadName 1", 5)); var t2 = new Thread(() => TravleThroughGates("threadName 2", 6)); var t3 = new Thread(() => TravleThroughGates("threadName 3", 12)); t1.Start(); t2.Start(); t3.Start(); Thread.Sleep(TimeSpan.FromSeconds(6)); Console.WriteLine("The gates are now open!"); mainEvent.Set();// Thread.Sleep(TimeSpan.FromSeconds(2)); mainEvent.Reset();//将事件状态设置为非终止,从而导致线程受阻 Console.WriteLine("The gates have been closed!"); Thread.Sleep(TimeSpan.FromSeconds(10)); Console.WriteLine("The gates are now open for the seconds time!"); mainEvent.Set(); Thread.Sleep(TimeSpan.FromSeconds(2)); Console.WriteLine("The gates have been closed!"); mainEvent.Reset(); }
这里启动了三个线程,同时进行等待。ManualResetEventSlim 的set是允许准备好的线程接受信号并继续工作。
CountDownEvent类
CountDownEvent信号类来等待直到一定数量的操作完成。
static CountdownEvent countdownEvent = new CountdownEvent(2); static void PerformOperation(string message,int seconds) { Thread.Sleep(TimeSpan.FromSeconds(seconds)); Console.WriteLine(message); countdownEvent.Signal(); } static void Main(string[] args) { Console.WriteLine("Starting two operations"); var t1 = new Thread(() => PerformOperation("Operation 1 is completd", 4)); var t2 = new Thread(() => PerformOperation("Operation 2 is completd", 8)); t1.Start(); t2.Start(); countdownEvent.Wait(); Console.WriteLine("Both operation have been completd"); countdownEvent.Dispose(); }
启动两个线程,当它们执行完成后会发生信号。一旦第二个线程完成,主程序会从等待CountdownEvent的状态中返回并继续执行。当针对需要等待多个异步操作的情形,方法非常遍历。当然如果某个线程出现死锁等情况,一直等待下,那么会一直等待。
Barrier类
Barrier类组织多个线程及时在某个时刻碰面。并提供了一个回调函数,每次线程调用了SignalAndWait方法后该回调函数会执行
static Barrier barrier = new Barrier(2, b => Console.WriteLine("End of phase {0}", b.CurrentPhaseNumber + 1)); static void PlayMusic(string name,string messages,int seconds) { for (int i = 0; i < 3; i++) { Console.WriteLine("-----------------------------"); Thread.Sleep(TimeSpan.FromSeconds(seconds)); Console.WriteLine("{0} starts to {1}", name, messages); Thread.Sleep(TimeSpan.FromSeconds(seconds)); Console.WriteLine("{0} finshes to {1}", name, messages); barrier.SignalAndWait(); } } static void Main(string[] args) { var t1 = new Thread(() => PlayMusic("the guitarist", "play an amazing solo", 5)); var t2 = new Thread(() => PlayMusic("the singer", "sing his song", 2)); t1.Start(); t2.Start(); }
使用Barrier,指定我们想同步两个线程。这两个线程任意一个调用了SignalAndWait方法后,会执行一个回调来打印出阶段
ReaderWriterLockSlim
ReaderWriterLockSlim类来创建一个线程安全的机制,在多线程中对一个集合进行读写操作。ReaderWriterLockSlim代表一个管理资源访问的锁,允许多个线程同时读取,以及独占写。
static ReaderWriterLockSlim readerWriterLockSlim = new ReaderWriterLockSlim(); static Dictionary<int, int> items = new Dictionary<int, int>(); static void Read() { Console.WriteLine("Reader Contexts of a dictionary"); while(true) { try { readerWriterLockSlim.EnterReadLock(); foreach (var key in items.Keys) { Thread.Sleep(TimeSpan.FromSeconds(0.1)); } } finally { readerWriterLockSlim.ExitReadLock(); } } } static void Writer(string threadName) { while(true) { try { int newkey = new Random().Next(250); readerWriterLockSlim.EnterUpgradeableReadLock(); if(!items.ContainsKey(newkey)) { try { readerWriterLockSlim.EnterWriteLock(); items[newkey] = 1; Console.WriteLine("New key {0} is added to a dictionary by a {1}",newkey, threadName); } finally { readerWriterLockSlim.ExitWriteLock(); } } Thread.Sleep(TimeSpan.FromSeconds(0.1)); } catch (Exception) { readerWriterLockSlim.ExitUpgradeableReadLock(); } } } static void Main(string[] args) { new Thread(Read) { IsBackground = true }.Start(); new Thread(Read) { IsBackground = true }.Start(); new Thread(Read) { IsBackground = true }.Start(); new Thread(() => Writer("Thread 1")){ IsBackground = true }.Start(); new Thread(() => Writer("Thread 2")) { IsBackground = true }.Start(); Thread.Sleep(TimeSpan.FromSeconds(30)); }
同时运行了三个程序来从字典中读取数据,还有另外两个线程向该字典中写入数据,使用ReaderWriterLockSlim类实现线程安全。
这里使用两种锁:读锁允许多线程读取数据,写锁在被释放前会阻塞了其他线程的所有操作,先获取读锁后读取数据,如果发现必须修改底层集合,只需使用EnterWriterLock方法升级锁,然后快速执行写操作。
SpanWait类
如果不使用内核模型的方式来使线程等待。SpanWait是一个混合同步结构,设计为使用用户模式等待一段时间,然后切换到内核模式以节省CPU时间
static volatile bool isCompleted = false;//volatile允许多个线程访问,呈现最新的 static void UserModelWait() { while(!isCompleted) { Console.WriteLine("."); } Console.WriteLine("Waiting is complete"); } static void HybridSpinWait() { var w = new SpinWait(); while (!isCompleted) { w.SpinOnce(); Console.WriteLine(w.NextSpinWillYield); } Console.WriteLine("Waiting is complete"); } static void Main(string[] args) { var t1 = new Thread(UserModelWait); var t2 = new Thread(HybridSpinWait); Console.WriteLine("Running user mode waiting"); t1.Start(); Thread.Sleep(20); isCompleted = true; Thread.Sleep(TimeSpan.FromSeconds(1)); isCompleted = false; Console.WriteLine("Running hybrid SpinWait construct waiting"); Thread.Sleep(5); isCompleted = true; }
定义一个线程,将执行一个无止境的循环,直到20毫秒后主线程设置isCompleted变量为true,我们可以试验周期为20-30秒,通过windows任务管理器测量CPU的负载情况。
用volatie关键字来声明iscompleted静态字段,volatie字段不会被编辑器和处理器优化,只能被单个线程访问。
以上是关于多线程-3(同步)的主要内容,如果未能解决你的问题,请参考以下文章
阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第3节 线程同步机制_4_解决线程安全问题_同步代码块