信号量 semaphore

信号量(semaphore)
  Linux内核的信号量在概念和原理上与用户态的System V的IPC机制信号量是一样的,但是它绝不可能在内核之外使用,因此它与System V的IPC机制信号量毫不相干。
  信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源, 初始值为1就变成互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护的共享资源。
   一个任务要想访问共享资源,首先必须得到信号量,获取信号量的操作将把信号量的值减1,若当前信号量的值为负数,表明无法获得信号量,该任务必须挂起在该信号量的等待队列等待该信号量可用;若当前信号量的值为非负数,表示可以获得信号量,因而可以立刻访问被该信号量保护的共享资源。
  当任务访问完被信号量保护的共享资源后,必须释放信号量,释放信号量通过把信号量的值加1实现,如果信号量的值为非正数,表明有任务等待当前信号量,因此它也唤醒所有等待该信号量的任务。
  信号量的API有:
DECLARE_MUTEX(name)  该宏声明一个信号量name并初始化它的值为0,即声明一个互斥锁。
 
DECLARE_MUTEX_LOCKED(name)  该宏声明一个互斥锁name,但把它的初始值设置为0,即锁在创建时就处在已锁状态。因此对于这种锁,一般是先释放后获得。
 
void sema_init (struct semaphore *sem, int val);  该函用于数初始化设置信号量的初值,它设置信号量sem的值为val。
 
void init_MUTEX (struct semaphore *sem);   该函数用于初始化一个互斥锁,即它把信号量sem的值设置为1。
 
void init_MUTEX_LOCKED (struct semaphore *sem);   该函数也用于初始化一个互斥锁,但它把信号量sem的值设置为0,即一开始就处在已锁状态。
 
void down(struct semaphore * sem);   该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文(包括IRQ上下文和softirq上下文)使用该函数。该函数将把sem的值减1,如果信号量sem的值非负,就直接返回,否则调用者将被挂起,直到别的任务释放该信号量才能继续运行。
 
int down_interruptible(struct semaphore * sem);   该函数功能与down类似,不同之处为,down不会被信号(signal)打断,但down_interruptible能被信号打断,因此该函数有返回值来区分是正常返回还是被信号中断,如果返回0,表示获得信号量正常返回,如果被信号打断,返回-EINTR。
 
int down_trylock(struct semaphore * sem);    该函数试着获得信号量sem,如果能够立刻获得,它就获得该信号量并返回0,否则,表示不能获得信号量sem,返回值为非0值。因此,它不会导致调用者睡眠,可以在中断上下文使用。
 
void up(struct semaphore * sem);  该函数释放信号量sem,即把sem的值加1,如果sem的值为非正数,表明有任务等待该信号量,因此唤醒这些等待者。
 
转自:领测软件测试网[http://www.ltesting.net] 
原文链接:http://www.ltesting.net/ceshi/ruanjianceshikafajishu/rjcshjdj/windows/2007/0608/20523.html



线程间同步之 semaphore(信号量)

semaphore 可用于进程间同步也可用于同一个进程间的线程同步。

semaphore 非常类似于mutex ,

共同点:semaphore和mutex都是内核对象,都可用于进程间的同步,并且都特别占用系统资源(线程的同步包括用户模式下的同步和内核模式下的同步,如果用内核对象来同步被保护的资源,系统需要从用户模式切换到内核模式,这个时间大概是1000个cpu周期)。

区别为:mutex只能由一个线程(进行)访问被保护的资源。semaphore 是一种带计数的mutex的锁定,可定义同时访问被保护的资源的线程数。

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

信号量的规则如下:

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

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

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

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

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

1、线程间的sempahore同步

使用下面的例子来讲解(见注释部分):

复制代码
 1        static void Main(string[] args)
 2         {
 3             int threadCount = 6;
 4             int sempaphoreCount = 4;
 5             // maximumCount-initialCount  之间的差值为已经锁定的 semaphore的数量  此实例中已经指定占用了0个信号量
 6             //Semaphore的第三个参数为信号量的名称,如果设定了名称,则可用于进程间的同步,如果没有设置名称则只能用于进程内的线程同步
 7             System.Threading.Semaphore sempaphore = new System.Threading.Semaphore(sempaphoreCount, sempaphoreCount, "sempaphore");
 8 
 9             Thread[] threads = new Thread[threadCount];
10             for (int i = 0; i < threadCount; i++)
11             {
12                 threads[i] = new Thread(ThreadMain);
13                 threads[i].Start(sempaphore);
14             }
15             for (int i = 0; i < threadCount; i++)
16             {
17                 threads[i].Join();
18             }
19             Console.WriteLine("All threads finished!");
20             Console.ReadKey();
21         }
22 
23         /// <summary>
24         /// 线程执行的方法
25         /// </summary>
26         /// <param name="o"></param>
27         static void ThreadMain(object o)
28         {
29             Semaphore semaphore = o as Semaphore;
30             Trace.Assert(semaphore != null, "o must be a semphore type");
31 
32             bool isCompleted = false;
33             while (!isCompleted)
34             {
35                 //锁定信号量,如果锁定计数已经达到最高计数限制,则等待600毫秒。如果在600毫秒后未能获得锁定,则返回false。
36                 if (semaphore.WaitOne(600, false))
37                 {
38                     try
39                     {
40                         Console.WriteLine("Thread {0} locks the semaphore ", Thread.CurrentThread.ManagedThreadId);
41                         Thread.Sleep(2000);
42                     }
43                     finally
44                     {
45                         //解除资源的锁定。参数为退出信号量的次数。占用一个信号量故退出一个。
46                         semaphore.Release(1);
47                         Console.WriteLine("Thread {0} release the semaphore", Thread.CurrentThread.ManagedThreadId);
48                         isCompleted = true;
49                     }
50                 }
51                 else
52                 {
53                     Console.WriteLine("Timeot for thread {0}; wait again", Thread.CurrentThread.ManagedThreadId);
54                 }
55             }
56         }
复制代码

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

运行结果如下:

可以看到,四个线程(11、12、13、14)获得了锁定。线程15,16需要等待。该等待会重复进行,直到获得锁定的线程之一解除了信号量的锁定。

2、进程间的sempahore同步

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

大家先看代码:

复制代码
 1         static void Main(string[] args)
 2         {
 3             //定义可同步运行的可用实例数
 4             int CreateNew = 2;
 5 
 6             //定义可同步运行的最大实例数
 7             int MaxCreateNew = 5;
 8 
 9             // maximumCount-initialCount  之间的差值为已经锁定的 semaphore的数量  此实例中已经指定占用了3个信号量 计算方式为(MaxCreateNew-CreateNew)
10             //Semaphore的第三个参数为信号量的名称,如果设定了名称,则可用于进程间的同步,如果没有设置名称则只能用于进程内的线程同步
11             System.Threading.Semaphore sempaphore = new System.Threading.Semaphore(CreateNew, MaxCreateNew, "sempaphoreProcess");
12 
13             if (sempaphore.WaitOne(100, false))
14             {
15                 Console.WriteLine("系统正在运行……");
16                 Console.ReadKey();
17             }
18             else
19             {
20                 MessageBox.Show("当前已经有 " + CreateNew + " 个实例在运行,系统将退出!", "您好", MessageBoxButtons.OK, MessageBoxIcon.Information);
21             }
22 
23         }
复制代码

生成解决方案后运行三次程序:

第一次运行和第二次运行结果如下:

第三次运行结果如下:

 现在已经完成只允许有两个应用程序实例在同时运行。

注意:信号量的进程同步和信号量的应用程序的名称无关。只要使用了同样名称的信号量,他们之前就存在了一种协约。


线程同步之信号量Semaphore

阅读本篇之前推荐阅读以下姊妹篇:

秒杀多线程第四篇一个经典的多线程同步问题

秒杀多线程第五篇经典线程同步关键段CS

秒杀多线程第六篇经典线程同步事件Event

秒杀多线程第七篇经典线程同步互斥量Mutex

前面介绍了关键段CS事件Event互斥量Mutex在经典线程同步问题中的使用。本篇介绍用信号量Semaphore来解决这个问题。

首先也来看看如何使用信号量,信号量Semaphore常用有三个函数,使用很方便。下面是这几个函数的原型和使用说明。

1. CreateSemaphore

函数功能:创建信号量

函数原型:

函数说明:

第一个参数表示安全控制,一般直接传入NULL。

第二个参数表示初始资源数量。

第三个参数表示最大并发数量。

第四个参数表示信号量的名称,传入NULL表示匿名信号量。

2. OpenSemaphore

函数功能:打开信号量

函数原型:

函数说明:

第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。

第二个参数表示信号量句柄继承性,一般传入TRUE即可。

第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。

3. ReleaseSemaphore

函数功能:递增信号量的当前资源计数

函数原型:

函数说明:

第一个参数是信号量的句柄。

第二个参数表示增加个数,必须大于0且不超过最大资源数量。

第三个参数可以用来传出先前的资源计数,设为NULL表示不需要传出。

注意:当前资源数量大于0,表示信号量处于触发,等于0表示资源已经耗尽故信号量处于末触发。在对信号量调用等待函数时,等待函数会检查信号量的当前资源计数,如果大于0(即信号量处于触发状态),减1后返回让调用线程继续执行。一个线程可以多次调用等待函数来减小信号量。

4. 最后一个 信号量的清理与销毁

由于信号量是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。

在经典多线程问题中设置一个信号量和一个关键段。用信号量处理主线程与子线程的同步,用关键段来处理各子线程间的互斥。详见代码:

运行结果如下图:

可以看出来,信号量也可以解决线程之间的同步问题。

由于信号量可以计算资源当前剩余量并根据当前剩余量与零比较来决定信号量是处于触发状态或是未触发状态,因此信号量的应用范围相当广泛。本系列的《秒杀多线程第十篇 生产者消费者问题》将再次使用它来解决线程同步问题,欢迎大家参阅。

至此,经典线程同步问题全部结束了,下一篇《秒杀多线程第九篇 经典多线程同步问题总结》将会对其作个总结以梳理各知识点。

原文地址:http://blog.csdn.net/morewindows/article/details/7481609


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值