其實.NET中的訊號量(Semaphore)是作業系統維持的一個整數。當整數位0時。其他線程無法進入。當整數大於0時,線程可以進入。每當一個線程進入,整數-1,線程退出後整數+1。整數不能超過訊號量的最大請求數。訊號量在初始化的時候可以指定這個整數的初始值。
System.Threading.Semaphore類的建構函式的兩個參數第一個就是訊號量的內部整數初始值,也就是初始請求數,第二個參數就是最大請求數。
如果沒有額外操作訊號量的話,訊號量的運行限制就取決於這個初始請求數,比如下面代碼:
//+ using System.Threading;
static Semaphore semaphore;
//當前訊號量中線程數量
static int count;
//用於產生隨機數
static Random r;
static void Main()
{
r = new Random();
//初始化訊號量:初始請求數為1,最大請求數為3
semaphore = new Semaphore(1, 3);
//放出10個線程
for (int i = 0; i < 5; i++)
ThreadPool.QueueUserWorkItem(doo, i + 1);
Console.ReadKey(true);
}
static void doo(object arg)
{
int id = (int)arg;
PrintStatus(id, "等待");
semaphore.WaitOne();
PrintStatus(id, "進入");
PrintCount(1);
Thread.Sleep(r.Next(1000));
PrintStatus(id, "退出");
PrintCount(-1);
semaphore.Release();
}
//輸出線程狀態
static void PrintStatus(int id, string s)
{
Console.WriteLine("線程{0}:{1}", id, s);
}
//修改並輸出線程數量
static void PrintCount(int add)
{
Interlocked.Add(ref count, add);
Console.WriteLine("=> 訊號量值:{0}", Interlocked.Exchange(ref count, count));
}
輸出:
線程1:等待
線程2:等待
線程3:等待
線程1:進入
=> 訊號量值:1
線程4:等待
線程1:退出
=> 訊號量值:0
線程5:等待
線程2:進入
=> 訊號量值:1
線程2:退出
=> 訊號量值:0
線程5:進入
=> 訊號量值:1
線程5:退出
=> 訊號量值:0
線程4:進入
=> 訊號量值:1
線程4:退出
=> 訊號量值:0
線程3:進入
=> 訊號量值:1
線程3:退出
=> 訊號量值:0
可以看到,訊號量始終會限制線程數量為1。即初始請求數的值。
當然通過Semaphore.Release方法可以修改增加當前訊號量的內部請求數(而不是讓他一直等於初始請求數),當然內部請求數不能超過訊號量的最大請求數。
比如將上面代碼的Main函數修改成如下:
r = new Random();
//初始化訊號量:初始請求數為1,最大請求數為3
semaphore = new Semaphore(1, 3);
//放出10個線程
for (int i = 0; i < 5; i++)
ThreadPool.QueueUserWorkItem(doo, i + 1);
//將訊號量的請求數加2,這樣訊號量的請求數為:3
semaphore.Release(2);
Console.ReadKey(true);
如下輸出:
線程1:等待
線程1:進入
線程3:等待
線程4:等待
線程4:進入
=> 訊號量值:2
=> 訊號量值:1
線程3:進入
=> 訊號量值:3
線程2:等待
線程1:退出
=> 訊號量值:2
線程2:進入
=> 訊號量值:3
線程5:等待
線程2:退出
=> 訊號量值:2
線程5:進入
=> 訊號量值:3
線程3:退出
=> 訊號量值:2
線程5:退出
=> 訊號量值:1
線程4:退出
=> 訊號量值:0
訊號量的請求數限制為3.