using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace TheNightclub
{
public class Program
{
public static Semaphore Bouncer { get; set; }
public static void Main(string[] args)
{
// Create the semaphore with 3 slots, where 3 are available.
Bouncer = new Semaphore(3, 3);
// Open the nightclub.
OpenNightclub();
}
public static void OpenNightclub()
{
for (int i = 1; i <= 50; i++)
{
// Let each guest enter on an own thread.
Thread thread = new Thread(new ParameterizedThreadStart(Guest));
thread.Start(i);
}
}
public static void Guest(object args)
{
// Wait to enter the nightclub (a semaphore to be released).
Console.WriteLine("Guest {0} is waiting to entering nightclub.", args);
Bouncer.WaitOne();
// Do some dancing.
Console.WriteLine("Guest {0} is doing some dancing.", args);
Thread.Sleep(500);
// Let one guest out (release one semaphore).
Console.WriteLine("Guest {0} is leaving the nightclub.", args);
Bouncer.Release(1);
}
}
}
thread A{
semaphore &s; //locks/semaphores are passed by reference! think about why this is so.
A(semaphore &s): s(s){} //constructor
foo(){
...
s.P();
;// some block of code B2
...
}
//thread B{
semaphore &s;
B(semaphore &s): s(s){} //constructor
foo(){
...
...
// some block of code B1
s.V();
..
}
main(){
semaphore s(0); // we start the semaphore at 0 (closed)
A a(s);
B b(s);
}
13 回答
把信号量想象成夜总会的保镖 . 俱乐部一次允许有一定数量的人 . 如果俱乐部已经满员,则不允许任何人进入,但只要一个人离开另一个人就可以进入 .
它只是一种限制特定资源的消费者数量的方法 . 例如,限制应用程序中对数据库的同时调用次数 .
这是C#中一个非常教学的例子:-)
Michael Barr撰写的文章Mutexes and Semaphores Demystified是对互斥体和信号量不同的简短介绍,以及它们应该和不应该使用的时间 . 我在这里摘录了几个关键段落 .
关键是应该使用互斥锁来保护共享资源,而信号量应该用于信令 . 您通常不应使用信号量来保护共享资源,也不应使用互斥信号来保护信令 . 例如,在使用信号量来保护共享资源方面,有一些问题与保镖类比 - 你可以这样使用它们,但它可能导致难以诊断错误 .
...
在这一点上,一个有趣的类比是使用浴室钥匙的概念作为保护共享资源 - 浴室 . 如果商店有一个单独的浴室,那么一把钥匙就足以保护该资源并防止多人同时使用它 .
如果有多个浴室,人们可能会想要对它们进行相似的键并制作多个键 - 这类似于信号量被误用 . 一旦你有一把钥匙,你实际上并不知道哪个浴室可用,如果沿着这条路走下去,你可能最终会使用互斥体来提供这些信息,并确保你没有带一个已经被占用的浴室 .
信号量是保护几个基本相同资源的错误工具,但这是有多少人想到它并使用它 . 保镖的类比明显不同 - 没有几种相同类型的资源,而是有一种资源可以接受多个同时使用的用户 . 我认为在这种情况下可以使用信号量,但很少有真实情况下类比实际存在 - 更常见的是有几种相同类型,但仍然是个别资源,如浴室,不能使用这条路 .
...
...
这里有一个重要的观点,即互斥体以一种糟糕的方式干扰实时操作系统,导致优先级倒置,其中由于资源共享,可能在更重要的任务之前执行不太重要的任务 . 简而言之,当较低优先级的任务使用互斥锁来获取资源A时,会发生这种情况,然后尝试抓取B,但由于B不可用而暂停 . 当它正在等待时,一个更高优先级的任务出现并且需要A,但是它已经被捆绑了,并且由于它正在等待B而没有运行的进程 . 有很多方法可以解决这个问题,但它通常是修复的通过改变互斥锁和任务管理器 . 在这些情况下互斥体比二进制信号量复杂得多,并且在这样的实例中使用信号量将导致优先级反转,因为任务管理器不知道优先级倒置并且不能用于纠正它 .
...
互斥:资源共享
信号量:信令
如果不仔细考虑副作用,请不要将其中一种用于另一种 .
Mutex:独占成员访问资源
信号量:n成员访问资源
也就是说,互斥锁可用于同步对计数器,文件,数据库等的访问 .
sempahore可以做同样的事情,但支持固定数量的同时呼叫者 . 例如,我可以将数据库调用包装在信号量(3)中,这样我的多线程应用程序将以最多3个同时连接命中数据库 . 所有尝试都将阻止,直到三个插槽中的一个打开 . 他们做的事情就像做天真的节流真的很容易 .
@Craig:
这不仅限于一个线程 . 信号量可以配置为允许固定数量的线程访问资源 .
信号量也可以用作...信号量 . 例如,如果有多个进程将数据排入队列,并且只有一个任务从队列中消耗数据 . 如果您不希望您的使用任务不断轮询队列中的可用数据,则可以使用信号量 .
这里信号量不是用作排除机制,而是用作信令机制 . 消耗任务正在等待信号量生成任务在信号量上发布 .
这样,当且仅当有数据要出列时,消耗任务才会运行
构建并发程序有两个基本概念 - 同步和互斥 . 我们将看到这两种类型的锁(信号量通常是一种锁定机制)如何帮助我们实现同步和互斥 .
信号量是一种编程结构,它通过实现同步和互斥来帮助我们实现并发 . 信号量有两种类型,Binary和Counting .
信号量有两部分:计数器和等待访问特定资源的任务列表 . 信号量执行两个操作:wait(P)[这就像获取锁定],释放(V)[类似于释放锁定] - 这是人们可以对信号量执行的唯一两个操作 . 在二进制信号量中,计数器逻辑上介于0和1之间 . 您可以将其视为类似于具有两个值的锁:打开/关闭 . 计数信号量具有多个计数值 .
重要的是要理解信号量计数器跟踪不必阻止的任务数量,即它们可以取得进展 . 任务阻止,仅在计数器为零时将自己添加到信号量列表中 . 因此,如果任务无法进行,则会将任务添加到P()例程的列表中,并使用V()例程“释放” .
现在,很明显看到二进制信号量如何用于解决同步和互斥 - 它们本质上是锁 .
恩 . 同步:
在上面的例子中,B2只能在B1完成执行后执行 . 假设线程A首先执行 - 获取sem.P(),并等待,因为计数器为0(关闭) . 线程B出现,完成B1,然后释放线程A - 然后完成B2 . 所以我们实现了同步 .
现在让我们看看使用二进制信号量的互斥:
互斥也很简单 - m1和m2不能同时进入临界区 . 因此,每个线程使用相同的信号量为其两个关键部分提供互斥 . 现在,是否有可能获得更大的并发性?取决于关键部分 . (想想如何使用信号量来实现互斥 . 暗示提示:我是否只需要使用一个信号量?)
计数信号量:具有多个值的信号量 . 让我们来看看这意味着什么 - 一个具有多个值的锁?那么开放,封闭,......嗯 . 互斥或同步的多阶段锁定有什么用?
让我们更容易两个:
使用计数信号量进行同步:假设您有3个任务 - 您希望在3之后执行#1和2 . 您将如何设计同步?
因此,如果您的信号量开始关闭,则确保t1和t2阻塞,并添加到信号量列表中 . 然后沿着所有重要的t3,完成其业务并释放t1和t2 . 他们被释放的顺序是什么?取决于信号量列表的实现 . 可以是FIFO,可以基于某些特定的优先级等 . (注意:考虑如何安排你的P和V;如果你想要以某种特定的顺序执行t1和t2,并且你不知道信号量的实现)
(找出:如果V的数量大于P的数量,会发生什么?)
相互排斥使用计数信号量:我希望你为此构建自己的伪代码(让你更好地理解事物!) - 但基本概念是这样的:计数器的计数信号量= N允许N个任务自由地进入临界区 . 这意味着你有N个任务(或线程,如果你愿意)进入关键部分,但是第N个任务被阻止(进入我们最喜欢的被阻止任务列表),只有当有人V是信号量时才通过至少一次 . 所以信号量计数器,而不是在0和1之间摆动,现在介于0和N之间,允许N个任务自由进入和退出,阻止任何人!
天啊,为什么你需要这么蠢的东西?互不排斥的重点不是让不止一个人访问资源吗? (提示提示......你的计算机中并不总是只有一个驱动器,你......?)
想一想:单独计算信号量是否可以实现互斥?如果您有10个资源实例,并且有10个线程进入(通过计数信号量)并尝试使用第一个实例,该怎么办?
考虑一下,可以容纳包括驾驶员在内的总共3(后)2(前)人的出租车 . 因此, semaphore 一次只允许5个人在车内 . 并且 mutex 只允许一个人坐在汽车的一个座位上 .
因此, Mutex 允许资源(如OS线程)的独占访问,而 Semaphore 允许一次访问 n 资源 .
信号量是包含自然数(即,大于或等于零的整数)的对象,在该自然数上定义了两个修改操作 . 一项操作,
V
,为自然增加了1 . 另一个操作P
将自然数减1.这两个活动都是原子的(即没有其他操作可以与V
或P
同时执行) .因为自然数0不能减少,所以在包含0的信号量上调用
P
将阻止调用进程(/ thread)的执行,直到某个时刻数字不再为0并且P
可以成功(并且原子地)执行 .如其他答案中所述,信号量可用于将对特定资源的访问限制为最大(但可变)数量的进程 .
硬件或软件标志 . 在多任务系统中,信号量是变量,其值指示公共资源的状态 . 需要资源的过程检查信号量以确定资源状态,然后决定如何继续 .
所以想象一下每个人都想去洗手间,浴室里只有一定数量的钥匙 . 现在如果没有足够的钥匙,那个人需要等待 . 因此,将信号量视为代表可用于浴室的一组密钥(系统资源),不同的进程(浴室参与者)可以请求访问 .
现在想象两个过程试图同时去洗手间 . 这不是一个好的情况,信号量用于防止这种情况 . 不幸的是,信号量是一种自愿的机制和过程(我们的浴室参与者)可以忽略它(即使有钥匙,有人仍然可以打开门) .
二进制/互斥量和计数信号量之间也存在差异 .
查看http://www.cs.columbia.edu/~jae/4118/lect/L05-ipc.html的讲义 .
我已经创建了可视化,这有助于理解这个想法 . 信号量控制多线程环境中对公共资源的访问 .
产量
示例代码来自article
这是一个老问题,但信号量最有趣的用途之一是读/写锁,并没有明确提到它 .
r / w锁以简单的方式工作:为读者消耗一个许可,为编写者提供所有许可 . 实际上,一个简单的r / w锁定实现,但需要读取元数据(实际上两次),这可能成为一个瓶颈,仍然明显优于互斥锁或锁 .
另一个缺点是编写器也可以很容易地启动,除非信号量是公平的,或者写入在多个请求中获得许可,在这种情况下,它们需要在它们之间有明确的互斥 .
进一步read:
信号量是一种锁定资源以保证资源的方法在执行一段代码时,只有这段代码可以访问该资源 . 这使得两个线程不会同时访问资源,这可能会导致问题 .