前篇咱们已经提过,内核模式构造比用户模式构造慢不少,一个缘由是它们要求Windows操做系统自身的配合,另外一个缘由是内核对象上调用的每一个方法都形成调用线程从托管代码转换为本机用户模式代码,再转换为背景内核模式代码,这些转换须要大量的CPU时间。面试
可是内核模式拥有用户模式没有的优势:windows
1. 当检测到资源竞争时,windows会阻塞输掉的线程并发
2. 可同步一台机器中不一样进程中运行的线程spa
3. 防止未经受权的帐户访问线程操作系统
4. 阻塞的线程能够指定超时值线程
事件和信号量就是两种内核模式线程同步构造。code
WaitHandle抽象基类,惟一的做用就是包装一个windows内核对象句柄,继承WaitHadle的对象能够分为三类:1. 事件构造(AutoResetEvent、ManualResetEvent) 2. Semaphore构造 3. Mutex构造对象
AutoResetEvent、ManualResetEvent:其基本工做原理是多个线程持有同一个XXXResetEvent,在这个XXXResetEvent未被set前,各线程都在WaitOne()除挂起;在这个XXXResetEvent被set后,全部被挂起的线程中有一个(AutoResetEvent的状况下)或所有(ManualResetEvent的状况下)恢复执行。blog
AutoResetEvent举例:继承
1 AutoResetEvent autoResetEvent = new AutoResetEvent(true); //true默认执行第一个未挂起的线程 2 3 Task.Factory.StartNew(() => 4 { 5 Console.WriteLine("线程1:我已经开始任务已经执行,等待指示"); 6 autoResetEvent.WaitOne(); 7 Console.WriteLine("线程1任务执行结束"); 8 autoResetEvent.Set(); 9 }); 10 11 12 Task.Factory.StartNew(() => 13 { 14 Console.WriteLine("线程2:我已经开始任务已经执行,等待指示"); 15 // Thread.Sleep(1000); 16 autoResetEvent.WaitOne(); 17 Console.WriteLine("线程2任务执行结束");; 18 }); 19 Console.WriteLine("程序结束"); 20 Console.ReadLine();
ManualResetEvent举例:
1 ManualResetEvent autoResetEvent = new ManualResetEvent(false); //true默认执行第一个未挂起的线程 2 3 Task.Factory.StartNew(() => 4 { 5 Console.WriteLine("线程1:我已经开始任务已经执行,等待指示"); 6 autoResetEvent.WaitOne(); 7 Console.WriteLine("线程1任务执行结束"); 8 9 }); 10 Task.Factory.StartNew(() => 11 { 12 Console.WriteLine("线程2:我已经开始任务已经执行,等待指示"); 13 // Thread.Sleep(1000); 14 autoResetEvent.WaitOne(); 15 Console.WriteLine("线程2任务执行结束");; 16 }); 17 Console.WriteLine("1s后再通知线程执行任务"); 18 Thread.Sleep(1000); 19 autoResetEvent.Set(); 20 Console.WriteLine("程序结束"); 21 Console.ReadLine();
记得有一个面试题,建立四个线程而且按照顺序打印ABCD重复三次,就考得是线程同步的问题,你们能够思考一下怎么解决。
构造原理:经过内核维护int32变量,信号量为0时,在信号量上等待的线程会阻塞,信号量大于0时,解除阻塞,在信号量上等待的线程接触阻塞时,内核自动从信号量的计数中减1.
1 Semaphore semaphore = new Semaphore(0, Int32.MaxValue); //指定最大并发 2 3 Task.Factory.StartNew(() => 4 { 5 Console.WriteLine("线程1:我已经开始任务已经执行,等待指示"); 6 semaphore.WaitOne(); 7 Console.WriteLine("线程1任务执行结束"); 8 semaphore.Release(); 9 10 }); 11 Task.Factory.StartNew(() => 12 { 13 Console.WriteLine("线程2:我已经开始任务已经执行,等待指示"); 14 // Thread.Sleep(1000); 15 semaphore.WaitOne(); 16 Console.WriteLine("线程2任务执行结束");; 17 }); 18 Console.WriteLine("1s后再通知线程执行任务"); 19 semaphore.Release(); 20 Console.WriteLine("程序结束"); 21 Console.ReadLine();
原理同AtuoResetEvent类似,都是一次只能释放一个正在等待的线程,可是互斥有一些额外的逻辑,这使得它比先前的构造更复杂。首先,Mutex对象会查询调用线程的Int32 ID,记录是哪个线程得到了它,一个线程调用ReleaseMutex时,mutex确保调用线程就是获取Mutex的那个线程,否则mutex对象的状态就会发生改变,另外,Mutex对象维护着一个递归计数,指出拥有该Mutex的线程拥有了它多少次,若是一个线程当前拥有一个mutex,然后该线程再次在mutex上等待,计数就会递增,这个线程容许继续运行。
构造Mutex的递归锁:
1 class MutexLock : IDisposable 2 { 3 private readonly Mutex mutex; 4 5 public MutexLock() 6 { 7 mutex = new Mutex(); 8 } 9 10 public void Enter() 11 { 12 mutex.WaitOne(); 13 //随便坐任何事情 14 Leave(); 15 mutex.ReleaseMutex(); 16 17 } 18 19 public void Leave() 20 { 21 mutex.WaitOne(); 22 //随便坐任何事情 23 mutex.ReleaseMutex(); 24 } 25 26 public void Dispose() 27 { 28 mutex.Dispose(); 29 } 30 }