Semaphore(信号量)

  场景:当多个任务或线程并行运行时,难以避免的对某些有限的资源进行并发的访问

  可以考虑使用信号量来进行这方面的控制(System.Threading.Semaphore)是表示一个Windows内核的信号量对象(操作系统级别,可以跨进程或AppDomain)。如果预计等待的时间较短,使用SemaphoreSlim(单进程)带来的开销更小。关于两者的区别如下:

  System.Threading.Semaphore 类表示一个命名(系统范围内)或本地信号量。它是环绕 Win32 信号量对象的精简包装器。Win32 信号量是计数信号量,该可用于控制对资源池的访问。

  SemaphoreSlim 类表示一个轻量、快速的信号量,可在等待时间预计很短的情况下用于在单个进程内等待。 SemaphoreSlim 尽可能多地依赖公共语言运行时 (CLR) 提供的同步基元。但是,它还提供延迟初始化、基于内核的等待句柄,作为在多个信号量上进行等待的必要支持。 SemaphoreSlim 也支持使用取消标记,但不支持命名信号量或使用用于同步的等待句柄。Semaphore的WaitOne或者Release方法的调用大约会耗费1微秒的系统时间,而优化后的SemaphoreSlim则需要大致四分之一微秒。在计算中大量频繁使用它的时候SemaphoreSlim还是优势明显,加上SemaphoreSlim还丰富了不少接口,更加方便我们进行控制,所以在4.0以后的多线程开发中,推荐使用SemaphoreSlim(相关链接:https://msdn.microsoft.com/zh-cn/library/z6zx288a(v=vs.110).aspx)

  这里我们只讨论System.Threading.Semaphore,.Net FrameWork中的信号量通过跟踪进入和离开的任务或线程来协调对资源的访问。号量需要知道资源的最大数量,当一个任务进入时,资源计数器会被减1(获取到访问许可证),当计数器为0时,如果有任务访问资源(未被授权访问许可证),它会被阻塞,直到有任务离开为止

  介绍一下Semaphore这个类的常用的初始化构造方法,如下,请仔细阅读注释部分。

   /// <summary>
    /// Initializes a new instance of the <see cref="T:System.Threading.Semaphore"/> class, specifying the maximum number of concurrent entries and optionally reserving some entries.
    /// </summary>
    /// <param name="initialCount">The initial number of requests for the semaphore that can be granted concurrently.</param><param name="maximumCount">The maximum number of requests for the semaphore that can be granted concurrently.</param><exception cref="T:System.ArgumentException"><paramref name="initialCount"/> is greater than <paramref name="maximumCount"/>.</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="maximumCount"/> is less than 1.-or-<paramref name="initialCount"/> is less than 0.</exception>
    [SecuritySafeCritical]
    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    public Semaphore(int initialCount, int maximumCount);

  initialCount:信号量可以接受的并发请求数量的初始容量

  maximumCount:信号量可以接受的并发请求数量的最大容量

  那么看下面这个demo。

 1 class Program
 2     {
 3         //创建一个可授权2个许可证的信号量,且初始值为2
 4         static Semaphore _semaphore = new Semaphore(2, 2);
 5 
 6         static void Main(string[] args)
 7         {
 8             Task.Factory.StartNew(() => DoWork());
 9             Task.Factory.StartNew(() => DoWork());
10             Task.Factory.StartNew(() => DoWork());
11 
12             Console.ReadLine();
13         }
14 
15         static void DoWork()
16         {
17             try
18             {
19                 Console.WriteLine(string.Format("Thread {0} 正在等待一个许可证……", Thread.CurrentThread.ManagedThreadId));
20                 _semaphore.WaitOne();
21                 Console.WriteLine(string.Format("Thread {0} 申请到许可证……", Thread.CurrentThread.ManagedThreadId));
22                 Thread.Sleep(5000);
23                 Console.WriteLine(string.Format("Thread {0} 释放许可证……", Thread.CurrentThread.ManagedThreadId));
24             }
25             finally
26             {
27                 _semaphore.Release();
28             }
29         }
30     }
View Code

  多个线程申请获取许可证,其中线程A通过_semaphore.WaitOne拿到了一个许可证,进行initialCount-1操作,业务完成之后再调用_semaphore.Release释放了这个许可证,initialCount+1。如果设置最大并发数=1,那么当前仅有1个线程能够拿到这个许可,其他线程都处于阻塞状态,直到该线程释放为止。当然信号量不可能永久的阻塞在那里。信号量也提供了超时处理机制。方法是在Wait函数中传入一个超时等待时间 - Wait(int TIMEOUT)。当Wait返回值为false时表明它超时了。如果传入了 -1,则表示无限期的等待。

  关于信号量和锁/队列的概念

  信号量(Semaphore)是一种CLR中的内核同步对象。与标准的排他锁对象(Monitor,Mutex,SpinLock)不同的是,它不是一个排他的锁对象,它与SemaphoreSlim,ReaderWriteLock等一样允许多个有限的线程同时访问共享内存资源。Semaphore就好像一个栅栏,有一定的容量,当里面的线程数量到达设置的最大值时候,就没有线程可以进去。然后,如果一个线程工作完成以后出来了,那下一个线程就可以进去了。Semaphore的WaitOne或Release等操作分别将自动地递减或者递增信号量的当前计数值。当线程试图对计数值已经为0的信号量执行WaitOne操作时,线程将阻塞直到计数值大于0。
  互斥量(Mutex) ,互斥量表现互斥现象的数据结构,也被当作二元信号灯。一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源。
Mutex本质上说就是一把锁,提供对资源的独占访问,所以Mutex主要的作用是用于互斥。Mutex对象的值,只有0和1两个值。这两个值也分别代表了Mutex的两种状态。值为0, 表示锁定状态,当前对象被锁定,用户进程/线程如果试图Lock临界资源,则进入排队等待;值为1,表示空闲状态,当前对象为空闲,用户进程/线程可以Lock临界资源,之后Mutex值减1变为0。
  Enqueue和Lock实际上是一个事物的两个名字。他们都支持队列(queue)和并发(concurrency)。他们在队列中的管理方式是“先进先出”(FIFO)的方式。

原文地址:https://www.cnblogs.com/Mushrooms/p/5122187.html