概述: semaphores 的说明和使用
微软官方文档:
Semaphore Objects - Win32 apps | Microsoft Learn
Semaphores是解决各种 producer/consumer问题的关键要素。这种问题会存有一个缓冲区,可能在同一时间内被读出数据或被写入数据。
理论可以证明,mutex 是 semaphore 的一种退化。如果你产生一个semaphore 并令最大值为1,那就是一个 mutex。也因此, mutex又常被称为binary semaphore。在许多系统中, semaphores 常被使用, 因为 mutexes可能并不存在。在Win32中semaphores 被使用的情况就少得多,因为 mutex 存在的缘故。
创建 semaphore
createSemaphoreA 函数 (winbase.h) - Win32 apps | Microsoft Learn
123456HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpAttributes, // 安全属性, 可以设置NULL LONG lInitialCount, // 初始值, [0, lMaximumCount] LONG lMaximumCount, // 最大值, 这也就是在同一时间内能够锁住semaphore之线程的最多个数 LPCTSTR lpName // 名称, 其他线程或进程可以根据名称引用该信号量; NULL则产生无名称信号量);
如果成功就传回一个handle, 否则传回NULL. 不论哪一种情况,GetLastError()都会传回一个合理的结果. 如果指定的semaphore名称已经存在,则该函数还是成功的, GetLastError()会传回ERROR_ALREADY_EXISTS.
获得 semaphore
通过信号量名,获得信号量对象句柄
OpenSemaphoreW function (synchapi.h) - Win32 apps | Microsoft Learn
12345HANDLE OpenSemaphoreW( [in] DWORD dwDesiredAccess, // 访问权限,一般传入 SEMAPHORE_ALL_ACCESS [in] BOOL bInheritHandle, // 信号量句柄继承性,一般传入TRUE即可 [in] LPCWSTR lpName // 名称,不同进程中个线程可以通过名称来确保它们访问同一个信号量);
semaphore的现值代表的意义是目前可用的资源数, 如果semaphore的现值为1, 表示还有一个锁定动作可以成功; 如果现值为5, 就表示还有五个锁定动作可以成功. 每当一个锁定动作成功, semaphore的现值就会减1. 你可以使用任何一种 Wait() 函数(例如 WaitForSingleObject() )要求锁定一个semaphore. 因此, 如果semaphore的现值不为0, Wait() 函数会立刻返回.
一个线程可以反复调用 Wait() 函数以产生新的锁定. 这和mutex绝不相同:拥有mutex的线程不论再调用多少次 Wait() 函数, 也不会被阻塞住。
一旦semaphore的现值降到0, 就表示资源已经耗尽. 此时, 任何线程如果调用Wait…()函数, 必然要等待, 直到某个锁定被解除为止.
解除 semaphore
释放信号量
ReleaseSemaphore function (synchapi.h) - Win32 apps | Microsoft Learn
注:传入的句柄必须有 SEMAPHORE_MODIFY_STATE 权限,参考 同步对象安全性和访问权限 - Win32 apps | Microsoft Learn
12345BOOL ReleaseSemaphore( [in] HANDLE hSemaphore, // 信号量的句柄 [in] LONG lReleaseCount, // 表示增加个数,必须大于0且不超过最大资源数量 [out, optional] LPLONG lpPreviousCount // 可以用来传出先前的资源计数,设为NULL表示不需要传出);
ReleaseSemaphore函数会触发WaitForSingleObject函数。ReleaseSemaphore函数用于释放一个信号量对象的计数,而WaitForSingleObject函数用于等待一个信号量对象的计数。当ReleaseSemaphore函数被调用时,它会将信号量对象的计数增加,并且如果有一个线程正在等待这个信号量对象的计数,那么这个线程就会被唤醒。因此,ReleaseSemaphore函数会触发WaitForSingleObject函数。
补充说明
为什么 semaphore 要有一个初值
CreateSemaphore()的第二个参数是lInitialCount, 它的存在理由和CreateMutex()的bInitialOwner参数的存在理由是一样的. 如果你把初值设定为0, 你的线程就可以在产生semaphore之后进行所有必要的初始化工作. 待初始化工作完成后, 调用 ReleaseSemaphore()就可以把现值增加到其最大可能值.
Demo 多线程同步
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950#include