//创建信号量
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);
函数说明:
第一个参数表示安全控制,一般直接传入NULL。
第二个参数表示初始资源数量。
第三个参数表示最大并发数量。
第四个参数表示信号量的名称,传入NULL表示匿名信号量。
//打开信号量
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
函数说明:
第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示信号量句柄继承性,一般传入TRUE即可。
第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。
//递增信号量的当前资源计数
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
函数说明:
第一个参数是信号量的句柄。
第二个参数表示增加个数,必须大于0且不超过最大资源数量。
第三个参数可以用来传出先前的资源计数,设为NULL表示不需要传出。
当前资源数量大于0,表示信号量处于触发,等于0表示资源已经耗尽故信号量处于末触发。在对信号量调用等待函数时,等待函数会检查信号量的当前资源计数,如果大于0(即信号量处于触发状态),减1后返回让调用线程继续执行。一个线程可以多次调用等待函数来减小信号量。//信号量的清理与销毁
CloseHandle()
在经典多线程问题中设置一个信号量和一个关键段。用信号量处理主线程与子线程的同步,用关键段来处理各子线程间的互斥。详见代码:
#include <stdio.h>
#include <process.h>
#include <windows.h>
long lGlobVar = 0;//全局资源
//信号量与关键段
HANDLE hSemaphorePtrParam;
CRITICAL_SECTION csGlobVar;
unsigned int WINAPI ThreadProc(void* lpParam){
//由于创建线程是要一定的开销的,所以新线程并不能第一时间执行到这来
int iThreadNo = *(int*)lpParam;
ReleaseSemaphore(hSemaphorePtrParam, 1, NULL);//信号量++
Sleep(50);//Do Sth
EnterCriticalSection(&csGlobVar);
lGlobVar++;//处理全局资源
Sleep(50);//Do Sth
printf("线程编号:%d,全局变量:%d.\n", iThreadNo, lGlobVar);
LeaveCriticalSection(&csGlobVar);
return 0;
}
void main()
{
//初始化信号量和关键段 当前0个资源,最大允许1个同时访问
hSemaphorePtrParam = CreateSemaphore(NULL, 0, 1, NULL);
InitializeCriticalSection(&csGlobVar);
HANDLE hThreads[10];
for(int i = 0; i < 10; i++){//等子线程接收到参数时主线程可能改变了这个i的值
hThreads[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &i, 0, NULL);//创建线程
WaitForSingleObject(hSemaphorePtrParam, INFINITE);//等待信号量>0 ,信号量--
}
//保证子线程已全部运行结束
WaitForMultipleObjects(10, hThreads, TRUE, INFINITE);
for(int i = 0; i < 10; i++){
CloseHandle(hThreads[i]);//关闭线程句柄,使其内核对象计数减一
}
//销毁信号量和关键段
CloseHandle(hSemaphorePtrParam);
DeleteCriticalSection(&csGlobVar);
}
可以看出来,信号量也可以解决线程之间的同步问题。
由于信号量可以计算资源当前剩余量并根据当前剩余量与零比较来决定信号量是处于触发状态或是未触发状态,因此信号量的应用范围相当广泛。