信号量Semaphore常用有三个函数,使用很方便。下面是这几个函数的原型和使用说明。
第一个 CreateSemaphore
函数功能:创建信号量
函数原型:
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);
函数说明:
第一个参数表示安全控制,一般直接传入NULL。
第二个参数表示初始资源数量。
第三个参数表示最大并发数量。
第四个参数表示信号量的名称,传入NULL表示匿名信号量。
第二个 OpenSemaphore
函数功能:打开信号量
函数原型:
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
函数说明:
第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示信号量句柄继承性,一般传入TRUE即可。
第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。
第三个 ReleaseSemaphore
函数功能:递增信号量的当前资源计数
函数原型:
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
函数说明:
第一个参数是信号量的句柄。
第二个参数表示增加个数,必须大于0且不超过最大资源数量。
第三个参数可以用来传出先前的资源计数,设为NULL表示不需要传出。
注意:当前资源数量大于0,表示信号量处于触发,等于0表示资源已经耗尽故信号量处于末触发。
在对信号量调用等待函数时,等待函数会检查信号量的当前资源计数,如果大于0(即信号量处于触发状态),减1后返回让调用线程继续执行。
一个线程可以多次调用等待函数来减小信号量。
最后一个 信号量的清理与销毁
由于信号量是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。
信号量的使用规则:
1. 如果当前资源计数大于0,那么信号量处于触发状态;
2. 如果当前资源计数等于0,那么信号量处于未触发状态;
3. 系统绝对不会让当前资源计数变为负数;
4. 当前资源计数绝对不会大于最大资源计数。
在使用信号量的时候,不要把信号量对象的使用计数和它的当前资源计数混为一谈。
因为在创建该信号量句柄时,这个内核对象的使用计数为1,而当前资源计数我们可以自己设定。
0表示当前有0个资源可用,最大资源个数可以是计算机能表示的最大32位数。
为了获得对被保护资源的访问权,线程要调用一个等待函数并传入信号量的句柄。在内部,等待函数会检查信号量的当前资源计数,如果它的值大于0(信号量处于触发状态),那么函数会把计数器减1 并让调用线程继续运行。信号量最大的优势在于他们会以原子方式来执行这些测试和设置操作,也就是说,当我们向信号量请求一个资源的时候,操作系统会检查资源是否可用,并将可用资源的数量递减,整个过程不会被别的线程打断。
如果等待函数发现信号量的当前资源计数为0(信号量处于未触发状态),那么系统会让调用线程进入等待状态。
在经典多线程问题中设置一个信号量和一个关键段。用信号量处理主线程与子线程的同步,用关键段来处理各子线程间的互斥。
实例代码如下:
#include <iostream> #include <windows.h> #include <process.h> using namespace std; const int NUM = 10; int count; HANDLE semaphore; CRITICAL_SECTION sc; unsigned int __stdcall funcTh(PVOID pm) { int a = *((int *) pm); ReleaseSemaphore(semaphore, 1, NULL); EnterCriticalSection(&sc); count++; cout << "第" << a << "个线程" << " " << "全局资源值为" << count << endl; LeaveCriticalSection(&sc); return 0; } int main() { HANDLE handles[NUM]; semaphore = CreateSemaphore(NULL, 0, 5, NULL); InitializeCriticalSection(&sc); int i; for(i=0; i<NUM; i++) { handles[i] = (HANDLE) _beginthreadex(NULL, 0, funcTh, &i, 0, NULL); WaitForSingleObject(semaphore, INFINITE); } WaitForMultipleObjects(NUM, handles, true, INFINITE); DeleteCriticalSection(&sc); CloseHandle(semaphore); getchar(); return 0; }
运行结果如下: