多线程总结之旅(8):线程同步之信号量

时间:2022-08-06 15:13:03

  

  上一篇我们介绍了互斥体,这一篇介绍信号量。。。。。。。。。。。。

   一、什么是信号量

    信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。

 

信号量有一个使用计数器,这个使用计数器,是信号量的最大资源计数和当前资源计数的差值。


 

  二、信号量的使用原理?  

 

    a、如果当前资源计数大于0,那么信号量处于触发状态。

 

    b、如果当前资源计数等于0,那么信号量处于未触发状态。

 

    c、系统绝对不会让当前资源计数变为负数。

 

    d、当前资源计数绝对不会大于最大最大资源计数

 

 

  


  三、示例    

    1、线程间的sempahore同步

      如果出现 WaitOne 就一个要有对应的Release 即获取信号量后,一定要释放。

      如:6个进程需要同时使用打印机,而电脑上只有四台打印机,则打印机是被保护的资源,信号量为4。则需要用semaphore来同步。  

    

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SemaphoreClass
{
class Program
{
static void Main(string[] args)
{
int sourceCount = 4;//打印机数量
int threadCount = 6;//需要打印的线程数目

// maximumCount-initialCount 之间的差值为已经锁定的 semaphore的数量(即信号量使用计数器) 此实例中已经指定占用了0个信号量
//Semaphore的第三个参数为信号量的名称,如果设定了名称,则可用于进程间的同步,如果没有设置名称则只能用于进程内的线程同步
Semaphore sempaphore = new Semaphore(sourceCount, sourceCount, "sempaphore");

Thread[] threads
= new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
threads[i]
= new Thread(ThreadMain);
threads[i].Start(sempaphore);
}
for (int i = 0; i < threadCount; i++)
{
threads[i].Join();
}
Console.WriteLine(
"All threads finished!");
Console.ReadKey();
}

/// <summary>
/// 线程执行的方法
/// </summary>
/// <param name="o"></param>
static void ThreadMain(object o)
{
Semaphore semaphore
= o as Semaphore;
Trace.Assert(semaphore
!= null, "o must be a semphore type");

bool isCompleted = false;
while (!isCompleted)
{
//锁定信号量,如果锁定计数已经达到最高计数限制,则等待600毫秒。如果在600毫秒后未能获得锁定,则返回false。
if (semaphore.WaitOne(600, false))
{
try
{
Console.WriteLine(
"Thread {0} locks the semaphore ", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(
2000);
}
finally
{
//解除资源的锁定。参数为退出信号量的次数。占用一个信号量故退出一个。
semaphore.Release(1);
Console.WriteLine(
"Thread {0} release the semaphore", Thread.CurrentThread.ManagedThreadId);
isCompleted
= true;
}
}
else
{
Console.WriteLine(
"Timeot for thread {0}; wait again", Thread.CurrentThread.ManagedThreadId);
}
}
}
}
}

 

  2、进程间的sempahore同步

    下面的例子将使用信号量来同步进程,一个应用程序可以有二个实例运行(如果只允许有一个实例来运行,最优之选是mutex,其次才是信号量)。虽然这个例子不太实用,但完全可以说明semaphore的特性。

 

  注意:生成解决方案后运行三次生成EXE,就会看到结果。信号量的进程同步和信号量的应用程序的名称无关。只要使用了同样名称的信号量,他们之前就存在了一种协约。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SemaphoreProcess
{
class Program
{
static void Main(string[] args)
{
//定义可同步运行的可用实例数
int CreateNew = 2;

//定义可同步运行的最大实例数
int MaxCreateNew = 5;

// maximumCount-initialCount 之间的差值为已经锁定的 semaphore的数量 此实例中已经指定占用了3个信号量 计算方式为(MaxCreateNew-CreateNew)
//Semaphore的第三个参数为信号量的名称,如果设定了名称,则可用于进程间的同步,如果没有设置名称则只能用于进程内的线程同步
System.Threading.Semaphore sempaphore = new System.Threading.Semaphore(CreateNew, MaxCreateNew, "sempaphoreProcess");

if (sempaphore.WaitOne(100, false))
{
Console.WriteLine("系统正在运行……");
Console.ReadKey();
}
else
{
MessageBox.Show("当前已经有 " + CreateNew + " 个实例在运行,系统将退出!", "您好", MessageBoxButtons.OK, MessageBoxIcon.Information);
}

}
}
}

  

 

 

 

下一篇介绍事件。。。。。。。。。。。。