信号量(Semaphore)是一种CLR中的内核同步对象。与标准的排他锁对象(Monitor,Mutex,SpinLock)不同的是,它不是一个排他的锁对象,它与SemaphoreSlim,ReaderWriteLock等一样允许多个有限的线程同时访问共享内存资源。Semaphore就好像一个栅栏,有一定的容量,当里面的线程数量到达设置的最大值时候,就没有线程可以进去。然后,如果一个线程工作完成以后出来了,那下一个线程就可以进去了。Semaphore的WaitOne或Release等操作分别将自动地递减或者递增信号量的当前计数值。当线程试图对计数值已经为0的信号量执行WaitOne操作时,线程将阻塞直到计数值大于0。
Semaphore是表示一个Windows内核的信号量对象(操作系统级别,可以跨进程或AppDomain)。如果预计等待的时间较短,使用SemaphoreSlim(单进程)带来的开销更小。关于两者的区别如下:
System.Threading.Semaphore 类表示一个命名(系统范围内)或本地信号量。它是环绕 Win32 信号量对象的精简包装器。Win32 信号量是计数信号量,该可用于控制对资源池的访问。
SemaphoreSlim 类表示一个轻量、快速的信号量,可在等待时间预计很短的情况下用于在单个进程内等待。 SemaphoreSlim 尽可能多地依赖公共语言运行时 (CLR) 提供的同步基元。但是,它还提供延迟初始化、基于内核的等待句柄,作为在多个信号量上进行等待的必要支持。 SemaphoreSlim 也支持使用取消标记,但不支持命名信号量或使用用于同步的等待句柄。
Semaphore的WaitOne或者Release方法的调用大约会耗费1微秒的系统时间,而优化后的SemaphoreSlim则需要大致四分之一微秒。在计算中大量频繁使用它的时候SemaphoreSlim还是优势明显,所以在4.0以后的多线程开发中,推荐使用SemaphoreSlim。
在构造Semaphore时,最少需要2个参数。信号量的初始容量和最大的容量。
[SecuritySafeCritical] [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public Semaphore(int initialCount, int maximumCount);
initialCount:信号量可以接受的并发请求数量的初始容量
maximumCount:信号量可以接受的并发请求数量的最大容量
示例代码:
1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4 5 public class Example 6 { 7 private static SemaphoreSlim semaphore; 8 // A padding interval to make the output more orderly. 9 private static int padding; 10 11 public static void Main() 12 { 13 // Create the semaphore. 14 semaphore = new SemaphoreSlim(0, 3); 15 Console.WriteLine("{0} tasks can enter the semaphore.", 16 semaphore.CurrentCount); 17 Task[] tasks = new Task[5]; 18 19 // Create and start five numbered tasks. 20 for(int i = 0; i <= 4; i++) 21 { 22 tasks[i] = Task.Run( () => { 23 // Each task begins by requesting the semaphore. 24 Console.WriteLine("Task {0} begins and waits for the semaphore.", 25 Task.CurrentId); 26 semaphore.Wait(); 27 28 Interlocked.Add(ref padding, 100); 29 30 Console.WriteLine("Task {0} enters the semaphore.", Task.CurrentId); 31 32 // The task just sleeps for 1+ seconds. 33 Thread.Sleep(1000 + padding); 34 35 Console.WriteLine("Task {0} releases the semaphore; previous count: {1}.", 36 Task.CurrentId, semaphore.Release()); } ); 37 } 38 39 // Wait for half a second, to allow all the tasks to start and block. 40 Thread.Sleep(500); 41 42 // Restore the semaphore count to its maximum value. 43 Console.Write("Main thread calls Release(3) --> "); 44 semaphore.Release(3); 45 Console.WriteLine("{0} tasks can enter the semaphore.", 46 semaphore.CurrentCount); 47 // Main thread waits for the tasks to complete. 48 Task.WaitAll(tasks); 49 50 Console.WriteLine("Main thread exits."); 51 } 52 } 53 // The example displays output like the following: 54 // 0 tasks can enter the semaphore. 55 // Task 1 begins and waits for the semaphore. 56 // Task 5 begins and waits for the semaphore. 57 // Task 2 begins and waits for the semaphore. 58 // Task 4 begins and waits for the semaphore. 59 // Task 3 begins and waits for the semaphore. 60 // Main thread calls Release(3) --> 3 tasks can enter the semaphore. 61 // Task 4 enters the semaphore. 62 // Task 1 enters the semaphore. 63 // Task 3 enters the semaphore. 64 // Task 4 releases the semaphore; previous count: 0. 65 // Task 2 enters the semaphore. 66 // Task 1 releases the semaphore; previous count: 0. 67 // Task 3 releases the semaphore; previous count: 0. 68 // Task 5 enters the semaphore. 69 // Task 2 releases the semaphore; previous count: 1. 70 // Task 5 releases the semaphore; previous count: 2. 71 // Main thread exits.
Samaphore实现:https://blog.csdn.net/ma_jiang/article/details/78631038