1.简介
信号量内核对象用来对资源进行计数。与其他所有内核对象相同,它们也包含一个使用计数,但它们还包含另外两个32位值:一个最大资源计数和一个当前资源计数。
最大资源计数:表示信号量可以控制的最大资源数量。
当前资源计数:表示信号量当前可用资源的数量。
信号量的规则如下:
- 如果当前资源计数大于0,那么信号量处于触发状态。
- 如果当前资源计数等于0,那么信号量处于未触发状态。
- 系统绝对不会让当前资源计数变为负数。
- 当前资源计数绝对不会大于最大资源计数。
使用下面的函数用于创建信号量内核对象。
HANDLE CreateSemaphore(
PSECURITY_ATTRIBUTE psa,
LONG lInitialCount,
LONG lMaximumCount,
PCTSTR pszName
);
- psa:指向SECURITY_ATTRIBUTES结构的指针。
- lInitialCount:信号量对象的初始计数。
- lMaximumCount:信号量对象的最大计数。
- pszName:名称
调用OpenSemaphore函数,可以获得一个已经存在的信号量的句柄,该句柄与当前进程相关联。
HANDLE OpenSemaphore(
DWORD fdwAccess,
BOOL bInheritHandle,
PCTSTR pszName
);
调用ReleaseSemaphore函数,线程就能够递增信号量的当前资源计数,
BOOL ReleaseSemaphore(
HANDLE hsem,
LONG lReleaseCount,
PLONG plPreviousCount
);
2.简单调用
HANDLE sem = CreateSemaphore(NULL,0,5,NULL);
创建一个信号量,它的最大资源计数为5,但一开始只有0个资源可供使用,所以信号量未被触发。任何等待该信号量的线程将因此进入等待状态。
为了获得对被保护资源的访问权,线程要调用一个等待函数并传入信号量的句柄。在内部,等待函数会检查信号量的当前资源计数。
如果它大于0,那么函数会把计数器减1并让调用线程继续运行。信号量最大的优势在于它们会以原子方式来执行这些测试和设置操作。
如果它等于0,那么系统让调用线程进入等待状态。当另一个线程将信号量的当前资源计数递增时,系统会记得那个等待的线程,使它们变成可调度的状态(并将当前资源计数递减)。
3.示例
#include <windows.h>
#include <stdio.h>
#define MAX_SEM_COUNT 1
#define THREADCOUNT 12
HANDLE ghSemaphore;
DWORD WINAPI ThreadProc(LPVOID);
int main(void)
{
HANDLE aThread[THREADCOUNT];
DWORD ThreadID;
int i;
//创建信号量
ghSemaphore = CreateSemaphore(
NULL, // default security attributes
MAX_SEM_COUNT, // initial count
MAX_SEM_COUNT, // maximum count
NULL); // unnamed semaphore
if (ghSemaphore == NULL)
{
printf("CreateSemaphore error: %d\n", GetLastError());
return 1;
}
// 创建多线程
for (i = 0; i < THREADCOUNT; i++)
{
aThread[i] = CreateThread(
NULL, // default security attributes
0, // default stack size
(LPTHREAD_START_ROUTINE)ThreadProc,
NULL, // no thread function arguments
0, // default creation flags
&ThreadID); // receive thread identifier
if (aThread[i] == NULL)
{
printf("CreateThread error: %d\n", GetLastError());
return 1;
}
}
// Wait for all threads to terminate
WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);
// Close thread and semaphore handles
for (i = 0; i < THREADCOUNT; i++)
CloseHandle(aThread[i]);
CloseHandle(ghSemaphore);
return 0;
}
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
// lpParam not used in this example
UNREFERENCED_PARAMETER(lpParam);
DWORD dwWaitResult;
BOOL bContinue = TRUE;
while (bContinue)
{
// Try to enter the semaphore gate.
dwWaitResult = WaitForSingleObject(
ghSemaphore, // handle to semaphore
0L); // zero-second time-out interval
switch (dwWaitResult)
{
// The semaphore object was signaled.
case WAIT_OBJECT_0:
// TODO: Perform task
printf("Thread %d: wait succeeded\n", GetCurrentThreadId());
bContinue = FALSE;
// Simulate thread spending time on task
Sleep(5);
// Release the semaphore when task is finished
if (!ReleaseSemaphore(
ghSemaphore, // handle to semaphore
1, // increase count by one
NULL)) // not interested in previous count
{
printf("ReleaseSemaphore error: %d\n", GetLastError());
}
break;
// The semaphore was nonsignaled, so a time-out occurred.
case WAIT_TIMEOUT:
printf("Thread %d: wait timed out\n", GetCurrentThreadId());
break;
}
}
return TRUE;
}