情景一中,我主要介绍了用于解决资源争用时各种方式的区别,本篇文章我们将进一步介绍线程同步的第二种场景。
情景二:数量有限,先到先得
情景简介:与情景一类似,但是这次茅坑的数量不只一个。如果有需求的人数少于茅坑数量,那一切都很和谐。但是人数超过茅坑数量的时候该怎么办?多个人占用一个坑?
解决办法:当所有茅坑都客满的时候,其他人必须乖乖等在外面,只有当有人从里面出来的时候,下一个人才能进去。
问题抽象:当某一资源同一时刻允许一定数量的线程使用的时候,需要有个机制来阻塞多余的线程,直到资源再次变得可用。
线程同步方案:Semaphore、SemaphoreSlim
方案特性:限量供应;除所有者外,其他人无条件等待;先到先得(谁先进茅坑,谁先用,没有先后顺序)
各方案间的区别
Semaphore 的中文名称叫信号量,在学操作系统的时候肯定都会讲到。而扯到信号量,最常拿来举的就是生产者与消费者的例子。与上一篇一样,我不会在这里重复介绍一些网上多的是的内容,大家如果有兴趣,可以自己去找度娘、必硬或谷哥。
在继续阅读前,请确保你已经对用户模式构造、内核模式构造和混合模式构造有所了解,如果尚未了解,建议您先阅读情景一中相关章节。
内核模式(kernal-mode)
Semaphore 通过在构造函数中传入容器大小来限制一次性允许访问的线程数量。与情景一介绍中的 Mutex 一样属于内核模式,而且都拥有同一个祖宗:WaitHandle。但是与 Mutex 不一样的是,通过线程一获取的所有权,可以由线程二来释放。下面的写法是完全合法的。
Task.Factory.StartNew(() => { //类似于消费者 semaphore.WaitOne(); Thread.Sleep(5000); }); Thread.Sleep(1000); Task.Factory.StartNew(() => { //类似于生产者 semaphore.Release(); });
优点:提供数量限制的能力,可以跨进程使用。
//进程一 Semaphore s = new Semaphore(1, 1, "myname", out creaded); //进程二 Semaphore s = Semaphore.OpenExisting("myname");
缺点:速度慢于用户模式、混合模式构造,稍快于 mutex。
混合模式(hybrid-mode)
SemaphoreSlim 是 .Net 4.0 时引进的一个新类型,使用方式上类似 Semaphore,是轻量级的 Semaphore。不允许跨进程使用。
优点:提供数量限制的能力;速度快,优于 Semaphore。
缺点:不能跨进程使用。
总 结
本篇文章介绍的方法主要用于解决资源有限(数量大于一)时线程同步的问题。如果资源唯一,那利用 Semaphore s = new Semaphore(1,1) 的效果与创建一个 Mutex 的效果实际是一致的。而此时的 Semaphore 会有一个新名称叫 Binary Semaphore (二元信号量)。
那什么时候该使用 Semaphore, 什么时候使用 Mutex?
记住本系列强调的情景!如果用于资源争用,请使用 Mutex;如果用于限量使用请使用 Semaphore。
本文来自《C# 基础回顾: 线程同步的情景之二》