(3)若相加结果大于等于零,则从该信号的期待队列中激发一个等待进程,然后再返回原进程再次执行或进入进程调度。
使用信号量内核对象进行线程同步主要会用到CreateSemaphore()、OpenSemaphore()、 ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()等函数。其 中,CreateSemaphore()用来构建一个信号量内核对象,其变量原型为:
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性指针
LONG lInitialCount, // 初始计数
LONG lMaximumCount, // 最大计数
LPCTSTR lpName // 对象名指针
);
参数lMaximumCount是一个有符号32位值,定义了允许的最大资源计数,最大 取值不能超过4294967295。lpName参数可以为建立的信号量定义一个名字,由于其建立的是一个内核对象,因此在其它进程中可以借助该名字而得 到此信号量。OpenSemaphore()函数就能用来依据信号量名打开在其它进程中建立的信号量,函数原型如下:
HANDLE OpenSemaphore(
DWORD dwDesiredAccess, // 访问标志
BOOL bInheritHandle, // 继承标志
LPCTSTR lpName // 信号量名
);
程离开对共享资源的处理时,必须借助ReleaseSemaphore()来提高当 前只用资源计数。否则将要发生当前正在处理共享资源的实际线程数并没有达到应限制的数值,而其它线程却由于当前可用资源计数为0而却未能开启的状况。 ReleaseSemaphore()的函数原型为:
BOOL ReleaseSemaphore(
HANDLE hSemaphore, // 信号量句柄
LONG lReleaseCount, // 计数递增数量
LPLONG lpPreviousCount // 先前计数
);
该函数将lReleaseCount中的值添加给信号量的当前资源计数,一般将 lReleaseCount设置为1ccriticalsection释放,如果必须也可以设定其它的值。WaitForSingleObject()和 WaitForMultipleObjects()主要用在强行开启共享资源的轮询函数入口处,主要用来判定信号量的当前可用资源计数是否允许本线程的进 入。只有在当前可用资源计数值小于0时,被监视的信号量内核对象能够受到通知。
信号量的使用特征让其最适用于对Socket(套接字)程序中泛型的同步。例如,网络上 的HTTP服务器要对同一时间内访问同一页面的用户数加以限制,这时可以为没一个用户对服务器的页面请求修改一个线程,而页面则是待保护的共享资源,通过 使用信号量对泛型的同步作用可以保证在任一时刻无论有多少用户对某一页面进行访问,只有不小于设定的最大用户数量的线程无法进行访问,而其它的访问企图则 被挂起,只有在有用户退出对此页面的访问后才有也许进入。
在MFC中,通过CSemaphore类对信号量作了描述。该类只具备一个构造函数,可以构造一个信号量对象,并对初始资源计数、最大资源计数、对象名跟安全属性等进行初始化,其原型如下:
CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );
在构造了CSemaphore类对象后,任何一个访问受保护共享资源的线程都需要借助 CSemaphore从父类CSyncObject类继承受到的Lock()和UnLock()成员函数来访问或释放CSemaphore对象。与上面介 绍的几种通过MFC类保持线程同步的方式类似,通过CSemaphore类也可以将上面的线程同步代码进行改写,这两种使用信号量的线程同步方法无论是在 实现原理上而是从实现结果上都是完全一致的。
事件(Event)
事件对象也可以借助通知操作的方法来维持线程的同步。并且可以推动不同进程中的线程同步操作。
信号量包含的几个操作语义:
CreateEvent() 创建一个信号量
OpenEvent() 打开一个事件
SetEvent() 回置事件
WaitForSingleObject() 等待一个事件
WaitForMultipleObjects()等待多个事件
使用临界区只能同步同一进程中的线程,而使用事件内核对象则可以对进程外的线程进行同步,其前提是受到对此事件对象的访问权。可以借助OpenEvent()函数获得得到,其函数原型为:
HANDLE OpenEvent(
DWORD dwDesiredAccess, // 访问标志
BOOL bInheritHandle, // 继承标志
LPCTSTR lpName // 指向事件对象名的指针
);
如果事件对象已建立(在构建事件时必须选定事件名),函数将返回指定事件的句柄。对于那 些在建立事件时没有指定事件名的事件内核对象,可以借助使用内核对象的继承性或是调用DuplicateHandle()函数来调用 CreateEvent()以取得对选定事件对象的访问权。在获得至访问权后所进行的同步操作与在同一个进程中所进行的线程同步操作是一样的。
如果必须在一个线程中期待多个事件,则用 WaitForMultipleObjects()来等待。WaitForMultipleObjects()与 WaitForSingleObject()类似,同时监视位于赋值函数中的所有线程。这些被监视对象的句柄享有公平的优先权,任何一个句柄都不可能比其 他句柄具有更高的优先权。WaitForMultipleObjects()的函数原型为:
DWORD WaitForMultipleObjects(
DWORD nCount, // 等待句柄数
CONST HANDLE *lpHandles, // 句柄数组首地址
BOOL fWaitAll, // 等待标志
DWORD dwMilliseconds // 等待时间间隔
);
参数nCount指定了应等待的内核对象的数量,存放那些内核对象的泛型由 lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待模式进行了选定,为TRUE时当所有对象都被通知时函数能够返 回,为FALSE则即使其中任何一个得到通知就可以返回。dwMilliseconds在这里的作用与在WaitForSingleObject()中的 作用是完全一致的。如果期待超时,函数将返回WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到 WAIT_OBJECT_0+nCount-1中的某个值,则表明所有指定对象的状况均为已通知状态(当fWaitAll为TRUE时)或是用以减去 WAIT_OBJECT_0而受到发生通知的对象的索引(当fWaitAll为FALSE时)。如果返回值在WAIT_ABANDONED_0与 WAIT_ABANDONED_0+nCount-1之间,则表示所有指定对象的状况均为已通知,且其中大约有一个对象是被抛弃的互斥对象(当 fWaitAll为TRUE时),或是用以减去WAIT_OBJECT_0表示一个等待正常结束的互斥对象的索引(当fWaitAll为FALSE时)。
MFC为事件相关处理也提供了一个CEvent类,共包括有除构造函数外的4个成员变量 PulseEvent()、ResetEvent()、SetEvent()和UnLock()。在功能上分别比较与Win32 API的PulseEvent()、ResetEvent()、SetEvent()和CloseHandle()等变量。而构造函数则履行了原 CreateEvent()函数建立事件对象的职能,其函数原型为:
CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
按照此缺省设置将建立一个自动复位、初始状况为复位状态的没有名字的事件对象。封装后的CEvent类使用出来十分方便,
事件可以推动不同进程中的线程同步操作,并且可以便于的谋求多个线程的优先比较等待操作,例如写多个WaitForSingleObject来替代WaitForMultipleObjects从而让编程非常灵活。
总结:
1. 互斥量与临界区的作用相当相近,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以建立互斥量需要的资源更多,所以即使只为了在进程内部是用的话让 用临界区会带给速度上的优势并无法避免资源占用量。因为互斥量是跨进程的互斥量一旦被建立,就可以借助名字打开它。
2. 互斥量(Mutex),信号灯(Semaphore),事件(Event)都可以被跨越进程使用来进行同步数据操作,而其它的对象与数据同步操作无关,但 对于进程跟线程来讲,如果进程跟线程在运行状况则为无信号状态,在退出后为有信号状态。所以可以使用WaitForSingleObject来期待进程跟 线程退出。
3. 通过互斥量可以选定资源被独占的方法使用,但即使有以下一种情况通过互斥量就能够处理,比如目前一位用户购买了一份三个并发访问许可的系统,可以根 据用户订购的访问许可人数来决定有多少个线程/进程可同时进行操作,这时候如果运用互斥量就没有办法完成这个规定,信号灯对象可以说是一种资源计数 器。
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-141012-2.html
现在呢
谁信啊