信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。
public static class SemaphoreSample
{
// 初始信号量为0,最大数量为3
private static readonly Semaphore Sema = new Semaphore(0, 3);
public static void DoWork()
{
while (true)
{
// 减少1个信号量
// 如果信号量为0,则等待
Sema.WaitOne();
Console.WriteLine("Do Work");
}
}
public static void Run()
{
for (var i = 0; i < 5; i++)
{
var thread = new Thread(DoWork);
thread.Start();
}
// while (true)
// {
// // 增加3个信号量
// Sema.Release(3);
// Thread.Sleep(1000);
// }
}
}
混合锁和信号量实现生产消费者模型
public static class ProducerSample
{
private static readonly Queue<int> Queue = new Queue<int>();
private static readonly object Lock = new object();
private static readonly SemaphoreSlim Sema = new SemaphoreSlim(0, int.MaxValue);
public static void Consumer()
{
while (true)
{
Sema.Wait();
lock (Lock)
{
Console.WriteLine($"Data={Queue.Dequeue()},ThreadId={Thread.CurrentThread.ManagedThreadId},Count={Queue.Count}");
}
}
}
public static void Producer()
{
var job = 0;
while (true)
{
lock (Lock)
{
Queue.Enqueue(job++);
}
Sema.Release(1);
Thread.Sleep(100);
}
}
public static void Run()
{
for (var i = 0; i < 5; i++)
{
var consumer = new Thread(Consumer);
consumer.Start();
}
Producer();
}
}
无信号量版实现生产消费者模型
public static class MonitorProducerSample
{
private static readonly Queue<int> Queue = new Queue<int>();
private static readonly object Lock = new object();
public static void Consumer()
{
while (true)
{
lock (Lock)
{
while (Queue.Count == 0)
{
// 添加当前线程到等待队列,并且释放锁
// 线程一致等待被唤醒
Monitor.Wait(Lock);
// 唤醒以后,线程会重新获取锁,继续执行代码
}
Console.WriteLine($"Data={Queue.Dequeue()},ThreadId={Thread.CurrentThread.ManagedThreadId},Count={Queue.Count}");
}
}
}
public static void Producer()
{
var job = 0;
while (true)
{
lock (Lock)
{
Queue.Enqueue(job++);
// 唤醒 锁的线程等待列中的1个消费线程
Monitor.Pulse(Lock);
}
Thread.Sleep(100);
}
}
public static void Run()
{
for (var i = 0; i < 5; i++)
{
var consumer = new Thread(Consumer);
consumer.Start();
}
Producer();
}
}