多线程 -- 信号量和互斥量
来源:互联网 发布:软件行业研究报告 编辑:程序博客网 时间:2024/05/18 01:19
信号量内核对象
信号量内核对象用来进行资源计数,它包含一个使用计数、最大资源数、当前资源计数。最大资源数表示信号量可以控制的最大资源数量,当前资源数表示信号当前可用的资源
数量。
设想一个场景:需要开发一个服务器进程,最多同时运行5个线程来响应客户端请求,应该设计一个“线程池”。最开始的时候,5个线程都应该在等待状态,如果有一个客户端
请求到来,那么唤醒其中的一个线程以处理客户端请求,如果同时的请求数量为5,那么5个线程将全部投入使用,再多的请求应该被放弃。也就是说,随着客户端请求的增加,当前资源计数随之递减。
我们可能需要这样的一个内核对象来实现这个功能:初始化5个线程并同时等待一个内核对象触发,当一个客户端请求到来时,试图触发内核对象,这样5个线程中随机一个被
唤醒,并且自动使内核对象变为未触发。外部判断上限是否到达5。表面看来似乎用“自动重置的事件对象”即可实现这个功能啊,为什么要涉及到信号量呢?因为信号量还可以
控制一次唤醒多少个线程!!而且这个例子只是信号量的一个用途,后面我们会看到一个更实际的用途。
总结一下,信号量内核对象是这样的一种对象:它维护一个资源计数,当资源计数大于0,处于触发状态;资源计数等于0时,处于未触发状态;资源计数不可能小于0,也绝不
可能大于资源计数上限。下图展示了这种内核对象的特点:
如上图,只有资源计数>0时才是触发状态,资源=0时为未触发状态,而WaitForSingleObject成功将递减资源计数,调用ReleaseSemaphore将增加资源计数。
下面两个函数CreateSemaphore和CreateSemaphoreEx用于创建信号量对象:
HANDLE
WINAPI CreateSemaphore(
__in_opt LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
//内核对象安全描述符
__in
LONG
lInitialCount,
//资源计数的初始值
__in
LONG
lMaximumCount,
//资源计数的最大值
__in_opt
LPCTSTR
lpName
//内核对象命名
);
HANDLE
WINAPI CreateSemaphoreEx(
__in_opt LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
__in
LONG
lInitialCount,
__in
LONG
lMaximumCount,
__in_opt
LPCTSTR
lpName,
__reserved
DWORD
dwFlags,
__in
DWORD
dwDesiredAccess
);
任何进程可以用OpenSemaphore来得到一个命名的信号量:
HANDLE
WINAPI OpenSemaphore(
__in
DWORD
dwDesiredAccess,
__in
BOOL
bInheritHandle,
__in
LPCTSTR
lpName
);
线程通过调用ReleaseSemaphore来递增资源计数,不一定每次只递增1,可以设置递增任意值。当将要超过资源上限值的时候,ReleaseSemaphore会返回FALSE。
BOOL
WINAPI ReleaseSemaphore(
__in
HANDLE
hSemaphore,
__in
LONG
lReleaseCount,
//可以设置递增的值
__out_opt
LPLONG
lpPreviousCount
//返回先前的资源计数
);
互斥量内核对象
互斥量(mutex)用来确保一个线程独占对一个资源的访问。互斥量包含一个使用计数、线程ID和一个递归计数,互斥量与关键段的行为几乎相同(因为它记录了线程ID和递
归计数,使得互斥量可以支持递归调用的情况)。互斥量的规则十分简单:如果线程ID为0(即没有线程独占它),那么它处于触发状态,任何试图等待该对象的线程都将获得
资源的独占访问;如果线程ID不为0,那么它处于未触发状态,任何试图等待该对象的线程都将等待。
可以使用CreateMutex或者CreateMutexEx创建互斥对象:
HANDLE
WINAPI CreateMutex(
__in_opt LPSECURITY_ATTRIBUTES lpMutexAttributes,
__in
BOOL
bInitialOwner,
//初始化对象的状态,如果传入FALSE则会初始化为触发状态,如果传入TRUE,那么对象的线程ID会被设置成当前调用线程,并初始化为未触发
__in_opt
LPCTSTR
lpName
);
HANDLE
WINAPI CreateMutexEx(
__in_opt LPSECURITY_ATTRIBUTES lpMutexAttributes,
__in_opt
LPCTSTR
lpName,
__in
DWORD
dwFlags,
__in
DWORD
dwDesiredAccess
);
一如既往,OpenMutex用于打开一个已经命名的互斥量内核对象:
HANDLE
WINAPI OpenMutex(
__in
DWORD
dwDesiredAccess,
__in
BOOL
bInheritHandle,
__in
LPCTSTR
lpName
);
线程在获得对独占资源的访问权限之后,可以正常执行相关的逻辑,当需要释放互斥对象的时候可以调用ReleaseMutex:
BOOL
WINAPI ReleaseMutex(
__in
HANDLE
hMutex
);
互斥量与其他内核对象不同,它会记录究竟是哪个线程占用了共享资源,结合递归计数,同一个线程可以在获得共享资源之后继续访问共享资源,这个行为就像关键段一样。然
而互斥量和关键段从本质上是不同的,关键段是用户模式的线程同步方法,而互斥量是内核模式的线程同步方式。
介绍完这两个内核对象后,我们思考一下前面在【Windows】线程漫谈——线程同步之Slim读/写锁中设计的一个场景:有一个共享的队列,2个服务端线程负责读取队列中的
条目以处理,2个客户端线程负责写入队列中的条目以使服务先端线程处理,当队列中没有条目的时候应当挂起服务端线程,直到有条目进入时才被唤醒,另一方面,当队列已
满时,客户端线程应当挂起直到服务端至少处理了一个条目,以释放至少一个条目的空间。
现在我们来用信号量和互斥量来实现同样的功能,下面的流程图分别是客户端写入线程和服务端读取线程的逻辑:
1.首先创建一个互斥量对象m_hmtxQ,并初始化为未触发状态;之后创建一个信号量对象,并设置最大资源计数为队列的长度,初始化资源计数为0,正好表征队列元素的个
数。
m_hmtxQ = CreateMutex(NULL,FALSE,NULL);
m_hsemNumElements = CreateSemaphore(NULL,0,nMaxElements,NULL);
2.设计客户端核心逻辑如下图:
WatiForSingleObject:试图获得队列的独占访问权限,对于这个队列无论是读还是写都应该是线程独占的。因此,使用互斥量对象来同步;
ReleaseSemaphore:试图增加一个资源计数,表征客户端想要向队列中增加一个元素,当然队列可能现在已经满了,对应的资源计数已达到计数上限,此时
ReleaseSemaphore会返回FALSE,这样客户端就不能像队列中插入元素。反之,如果ReleaseSemaphore返回TRUE,表示队列没有满,客户端可以向队列中插入元
素。
ReleaseMutex:无论客户端是否能够像队列中插入元素,在结束访问后,都应该释放互斥对象,以便其他线程能够进入临界资源。
3.设计服务端核心逻辑如下图:
WatiForSingleObject:试图获得队列的独占访问权限,对于这个队列无论是读还是写都应该是线程独占的。因此,使用互斥量对象来同步;
WaitForSingleObject(m_hsemNumElements…):试图检查信号量对象是否是触发状态。只有是触发状态的信号量对象,线程才能进入;也就意味着:队列中只要有元素
(资源>0,触发状态),服务端就能读取。反之,如果队列中没有元素(资源=0,未触发状态),服务端将暂时不能访问队列,这时应该立即释放Mutex。
ReleaseMutex:无论客户端是否能够像队列中插入元素,在结束访问后,都应该释放互斥对象,以便其他线程能够进入临界资源。
关于信号量的一个形象比喻:
以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
抽象的来讲,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。
在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。当一个线程调用Wait操作时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。
出处:http://www.cnblogs.com/P_Chou/archive/2012/07/13/semaphore-and-mutex-in-thread-sync.html
- 多线程 -- 信号量和互斥量
- 互斥量和信号量的区别【多线程概念】
- Linux多线程信号量的概念和使用
- linux 多线程信号量和互斥锁的使用
- 多线程信号量
- 多线程--信号量
- 多线程-信号量
- 理解互斥量和信号量
- 理解互斥量和信号量
- 理解互斥量和信号量
- 信号量和互斥量
- 理解互斥量和信号量
- 理解互斥量和信号量 .
- 互斥量和信号量
- 理解互斥量和信号量
- 理解互斥量和信号量
- 理解互斥量和信号量
- 理解互斥量和信号量
- Java Reflection Tutorial for Classes, Methods, Fields, Constructors, Annotations and much more
- stochastic regression
- 从服务器获取时间的年月日三级联动
- slf4j+log4j的初次使用
- 条款 22: 尽量用“传引用”而不用“传值”
- 多线程 -- 信号量和互斥量
- 一个简单Net 类似ORM工具
- 天龙八部回归记
- Android数据的四种存储方式SharedPreferences、SQLite、Content Provider和File (一) —— 总览
- ORACLE 用户密码过期
- spring Ioc 的学习
- ANROID SDK 搭建问题小结
- nginx配置多台tomcat目录
- 多线程 -- 线程基础