• 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)的方式。

  • 相关阅读:
    GitHub 集成在Windows Azure Web Site中
    WebMatrix 2发布了!添加了新的Windows Azure 功能
    客户文章: 10gen和微软合作伙伴在云端提供NoSql数据库
    VC++实现IP与ARP信息获取,可以同理实现APR攻击
    现实世界中的 Windows Azure: 刚刚起步的LiquidSpace借助Windows Azure快速发展
    VC++实现遍历所有进程的TCP与UDP链接
    Node.js 体验存储服务和服务运行时
    客户文章:Windows Azure SendGrid入门
    2005年大学毕业生的求职新战略
    WinAPI: RoundRect 绘制圆角矩形
  • 原文地址:https://www.cnblogs.com/Mushrooms/p/5122187.html
Copyright © 2020-2023  润新知