1. 概述
信号量内核对象用来对资源计数。与其他所有内核对象相同,它们也包含一个使用计数,但它们还包含另外两个32位值:一个最大资源和一个当前资源计数。最大资源计数表示信号量可以控制的最大资源,当前资源技术表示当前可用资源。
信号量的规则如下:
(1)如果当前资源计数大于0,那么信号量处于触发状态
(2)如果当前资源计数等于0,那么信号量属于未触发状态
(3)系统绝对不会让当前资源计数变为负数
(4)当前资源计数绝对不会大于最大资源计数
信号量与互斥量不同的地方是,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。
PV操作及信号量的概念都是由荷兰科学家E.W.Dijkstra提出的。信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用共享资源的进程数。
P操作申请资源:
(1)S减1;
(2)若S减1后仍大于等于零,则进程继续执行;
(3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转入进程调度。
V操作 释放资源:
(1)S加1;
(2)若相加结果大于零,则进程继续执行;
(3)若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转入进程调度。
当程序中向信号量请求一个资源的时候,操作系统会检查资源是否可用,并将可用资源的数量递减,整个过程不会被别的线程打断。只有当资源计数递减完成之后,系统才会允许另外一个线程请求对资源的访问。如果等待函数发现信号量的当前资源计数为0(信号量处于未触发状态),那么系统会让调用线程进去等待状态。当另外一个线程将信号量的当前资源计数递增时,系统会记得那个(或些)还在等待的线程,是他们编程可调度状态(并相应地递减当前资源计数)。
2. 函数API
2.1 创建信号量
HANDLE WINAPI CreateSemaphore(
_In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
_In_ LONG lInitialCount,
_In_ LONG lMaximumCount,
_In_opt_ LPCTSTR lpName
);
参数说明:
psa:安全控制,一般传入NULLlInitialCount:设置信号量的初始计数
lMaximumCount:设置信号量的最大计数
lpName:指定信号量对象的名称
2.2 释放信号量
BOOL WINAPI ReleaseSemaphore(
_In_ HANDLE hSemaphore,
_In_ LONG lReleaseCount,
_Out_opt_ LPLONG lpPreviousCount
);
参数说明:hSemaphore:信号量句柄
lReleaseCount:把lReleaseCount的值增加到当前资源技术上
plPreviousCount:返回当前资源计数的原始值
2.3 打开信号量
HANDLE WINAPI OpenSemaphore(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ LPCTSTR lpName
);
参数说明
dwDesiredAccess:下述常数之一:SEMAPHORE_ALL_ACCESS:要求对事件对象的完全访问;
SEMAPHORE_MODIFY_STATE:允许使用ReleaseSemaphore函数;
SYNCHRONIZE:允许同步使用信号机对象。
bInheritHandle:如果允许子进程继承句柄,则设为TRUE。
lpName:指定要打开的对象的名字。
3. 示例代码
DWORD WINAPI my_thread1(LPVOID m_pParameter); //用户线程1
DWORD WINAPI my_thread2(LPVOID m_pParameter); //用户线程2
UINT count = 0; //全局的计数函数
HANDLE m_hSem1, m_hSem2; //定义一个互斥量句柄
int _tmain(int argc, _TCHAR* argv[])
{
system("color f0");
int count_size(50);
m_hSem1 = ::CreateSemaphore(NULL, 0, 1, NULL); //初始化信号量
m_hSem2 = ::CreateSemaphore(NULL, 1, 1, NULL); //初始化信号量
HANDLE m_h1 = CreateThread(NULL, NULL, my_thread1, &count_size, NULL, NULL); //用户线程1
HANDLE m_h2 = CreateThread(NULL, NULL, my_thread2, &count_size, NULL, NULL); //用户线程2
::WaitForSingleObject(m_h1, INFINITE); //等待子线程结束
::WaitForSingleObject(m_h2, INFINITE); //等待子线程结束
::CloseHandle(m_hSem1); //关闭句柄
::CloseHandle(m_hSem2);
::CloseHandle(m_h1);
::CloseHandle(m_h2);
system("pause");
return 0;
}
//用户线程1
DWORD WINAPI my_thread1(LPVOID m_pParameter)
{
UINT* my_count = (UINT*)m_pParameter;
cout << "thread1" << endl;
for (int i = 0; i < *my_count; ++i)
{
::WaitForSingleObject(m_hSem1, INFINITE); //等待信号量
count = i;
cout << count << "\t";
ReleaseSemaphore(m_hSem2, 1, NULL); //释放信号量
}
return 0;
}
//用户线程2
DWORD WINAPI my_thread2(LPVOID m_pParameter)
{
UINT* my_count = (UINT*)m_pParameter;
cout << "thread2" << endl;
for (int i = 0; i < *my_count; ++i)
{
::WaitForSingleObject(m_hSem2, INFINITE); //等待信号量
count = i;
cout << count << "\t";
ReleaseSemaphore(m_hSem1, 1, NULL); //释放信号量
}
return 0;
}