信号量作用:用来保护关键代码段不被多线程并法调用(处理主线程与子线程的同步性问题)
信号量常用有三个函数:
第一个:创建信号量
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);
第一个参数:表示安全控制,一般直接传入NULL。
第二个参数:表示初始资源数量。
第三个参数:表示最大并发数量。
第四个参数:表示信号量的名称,传入NULL表示匿名信号量。
第二个:打开信号量
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
第一个参数:表示访问权限一般传入SEMAPHORE_ALL_ACCESS
第二个参数:信号量句柄的继承性一般传入SEMAPHORE_ALL_ACCESS
第三个参数:信号量的名字。确认不同进程的线程访问的是同一个信号量
第四个:递增信号量的当前资源计数
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
第一个参数:信号量的句柄
第二个参数:表示增加个数必须大于0,且小于最大资源数
第三个参数:传出先前资源数。NULL表示不需要传出
注意:当前资源数大于0,表示信号量处于触发。等于0表示信号已耗尽,处于未触发
在对信号量调用等待函数时,等待函数会检查信号量的当前资源计数,如果大于0(即信号量处于触发状态),减1后返回让调用线程继续执行。一个线程可以多次调用等待函数来减小信号量。
第五个:信号量的清理与销毁
CloseHandle()
信号量是内核函数所以可以用CloseHandle()进行销毁
信号量代码:
#include <stdio.h>#include <process.h>#include <windows.h>//定义信号量句柄
HANDLE g_hThreadSemaphore;//定义CS段
CRITICAL_SECTION g_csThreadCode;long g_num; //登录次数unsigned int __stdcall Fun(void *pPM); //线程函数const DWORD THREAD_NUM = 10;//启动线程数unsigned int __stdcall Fun(void *pPM){//由于创建线程是要一定的开销的,所以新线程并不能第一时间执行到这来
int nThreadNum = *(int *)pPM; //子线程获取参数EnterCriticalSection(&g_csThreadCode);//进入关键段
ReleaseSemaphore(g_hThreadSemaphore, 1, NULL);//信号量加
Sleep(50);//some work should to do
g_num++; //处理全局资源
Sleep(0);//some work should to do
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_num);
LeaveCriticalSection(&g_csThreadCode); //出关键段
return 0;
}int main()
{printf(" 信号量的使用\n");
g_num = 0;InitializeCriticalSection(&g_csThreadCode); //初始化关键段
CreateSemaphore(NULL,0,1,NULL);//0个资源,最大允许一个同时访问
HANDLE handle[THREAD_NUM];int i=0;
while (i<THREAD_NUM)
{handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);i++;WaitForSingleObject(g_hThreadSemaphore,INFINITE);//2.等待事件被触发
}WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);DeleteCriticalSection(&g_csThreadCode); //销毁关键段
for (i=0;i<THREAD_NUM;i++)
CloseHandle(handle[i]);return 0;
}运行结果:
由于信号量可以计算资源当前剩余量并根据当前剩余量与零比较来决定信号量是处于触发状态或是未触发状态,因此信号量的应用范围相当广泛