场景
在实际的使用过程中,存在只允许启动一个应用程序的情况。
效果
效果如下:当启动第一个时,可以起来一个实例,再次创建时,弹出提示框
解决方案
代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace CreateOnlyOne
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
bool creatNewOne = false;
using (new Semaphore(0, 1, "theOnlyOne", out creatNewOne))
{
if(creatNewOne)
{
Application.Run(new Form1());
}
else
{
MessageBox.Show("程序只能启动1个,当前已启动!无法再启动!");
}
}
}
}
}
分析
1、只能创建一个实例,说明需要在操作系统层有一个“登记表”的功能,已经在登过的,便会有记录。因此,在创建实例时,先判断是否有过登记,若没有,则创建一个实例,否则弹出提示。
2、Semaphore。可以用停车场的例子来说明Semaphore。
以一个停车场是运作为例。为了简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆不受阻碍的进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入一辆,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量(Semaphore)的作用。
更进一步,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。 当一个线程调用Wait(等待)操作时,它要么通过然后将信号量减一,要么一直等下去,直到信号量大于一或超时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为加操作实际上是释放了由信号量守护的资源。
其构造器如下:
在构造器中,最关键的有两个:
1、int initialCount:最开始时,看门人对外宣称可以进来的车辆数。MSDN上的解释:The initial number of requests for the semaphore that can be granted concurrently. 即最开始时,看门人能够一次能够处理的数量。
2、int maximumCount:停车场的最大数。
Semaphore例子
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace SemaphoreExample
{
class Program
{
static Semaphore sem = new Semaphore(2, 2, "PackingSemaphore");
static void Main(string[] args)
{
for (int i = 0; i < 5;i++ )
{
Thread td = new Thread(recordPacking);
td.Name = string.Format("车辆{0}", i);
td.Start(td.Name);
}
Console.ReadKey();
}
static void recordPacking(object obj)
{
sem.WaitOne();
Console.WriteLine(obj.ToString()+"进入停车场,时间:"+DateTime.Now.ToString());
Thread.Sleep(1500);
Console.WriteLine(obj.ToString() + "离开停车场,时间:" + DateTime.Now.ToString());
sem.Release();
}
}
}
其中有这样一句:
static Semaphore sem = new Semaphore(2, 2, “PackingSemaphore”);
代表,停车场有两个车位,可以同时进来两个车位。所以输出结果为:
当以上这句话,变为:
static Semaphore sem = new Semaphore(0, 2, “PackingSemaphore”);
Semaphore初始化时能处理的请求数为0,则车辆无法进入停车场。(相当于最开始看门人擅自离岗了,因此不管排多少车,一辆都进不去),如图所示:
即使是上面情况时,当加入释放的语句后。
static void Main(string[] args)
{
sem.Release(1); //*****就是这句****
for (int i = 0; i < 5; i++)
{
Thread td = new Thread(recordPacking);
td.Name = string.Format("车辆{0}", i);
td.Start(td.Name);
}
Console.ReadKey();
}
输出如下:
从上面的例子中可以得知Semaphore构造器中第一个参数initialCount的真正含义。
1、范围:限定在初始化。
2、Semaphore一次能够处理的请求数
总结
在知乎上有一个问题:semaphore和mutex的区别?
里面讲的不错,这其中涉及到Mutex与Semaphore的区别。其中“fleuria”的回答很好,无可辩驳。