什么是信号量
信号量(semaphore)是操作系统用来解决并发中的互斥和同步问题的一种方法。与互斥量不同的地方是,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。
信号量的工作原理以一个停车场为例,假设停车场只有三个车位,那么同一时刻最多只能有三辆车进入,还有其他车来时则必须在入口等待。只有当有一辆车离开停车场,才能允许其他车辆进入,如此往复。这个停车系统中,每辆车就好比一个线程,空车位数量就好比一个信号量,空车位数量限制了可以活动的线程。假如里面依然是三个车位,但是现在改变了规则,要求每次只能停两辆车,那么一开始进入两辆车,后面得等到有车离开才能有车进入,但是得保证最多停两辆车。对于Semaphore而言,就如同一个空车位数量,限制了可活动的线程数。
信号量的组成
- 计数器:该内核对象被使用的次数
- 最大资源数量:标识信号量可以控制的最大资源数量(带符号的32位)
- 当前资源数量:标识当前可用资源的数量(带符号的32位)。即表示当前开放资源的个数(注意不是剩下资源的个数),只有开放的资源才能被线程所申请。但这些开放的资源不一定被线程占用完。比如,当前开放5个资源,而只有3个线程申请,则还有2个资源可被申请,但如果这时总共是7个线程要使用信号量,显然开放的资源5个是不够的。这时还可以再开放2个,直到达到最大资源数量。
信号量的规则
信号量的规则如下:
(1)如果当前资源计数大于0,那么信号量处于触发状态(有信号状态),表示有可用资源。
(2)如果当前资源计数等于0,那么信号量属于未触发状态(无信号状态),表示没有可用资源。
(3)系统绝对不会让当前资源计数变为负数
(4)当前资源计数绝对不会大于最大资源计数
创建信号量
HANDLE WINAPI
CreateSemaphoreW(
_In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // Null 安全属性
_In_ LONG lInitialCount, //初始化时,共有多少个资源是可以用的。 0:未触发状//态(无信号状态),表示没有可用资源
_In_ LONG lMaximumCount, //能够处理的最大的资源数量 3
_In_opt_ LPCWSTR lpName //NULL 信号量的名称
);
- 第一个参数表示安全属性,这是创建内核对象函数都会有的参数,NULL表示默认安全属性
- 第二个参数表示初始时有多少个资源可用,0表示无任何资源(未触发状态)
- 第三个参数表示最大资源数
- 第四个参数表示信号量的名称,NULL表示无名称的信号量对象
增加/释放信号量
ReleaseSemaphore(
_In_ HANDLE hSemaphore, //信号量的句柄
_In_ LONG lReleaseCount, //将lReleaseCount值加到信号量的当前资源计数上面 0-> 1
_Out_opt_ LPLONG lpPreviousCount //当前资源计数的原始值
);
- 第一个参数表示信号量句柄,也就是调用创建信号量函数时返回的句柄
- 第二个参数表示释放的信号量个数,该值必须大于0,但不能大于信号量的最大计数
- 第三个参数表示指向要接收信号量的上一个计数的变量的指针。如果不需要上一个计数, 则此参数可以为NULL 。
关闭句柄
CloseHandle(
_In_ _Post_ptr_invalid_ HANDLE hObject
);
下面是一个简单程序示例:
#include<iostream>
#include<Windows.h>
#include<process.h>
using namespace std;
int main() {
HANDLE semaphore;
semaphore = CreateSemaphore(NULL, 0, 1, NULL);
CloseHandle(semaphore);
return 0;
}
上面这个程序创建了一个初始时0个资源,最大资源数为1的信号量。必须等待ReleaseSemaphore增加信号量后才能被使用。这种最大资源为1的信号量相当于一种特殊的互斥信号量。
资源信号量示例
下面这段程序创建了两个信号资源,其最大资源都为1;一个初始资源为0,另一个初始资源为1。线程中的for循环每执行一次会将另一个要申请的信号资源的可用资源数+1。因此程序的执行结果为两个线程中的for循环交替执行。
#include<iostream>
#include<Windows.h>
#include<process.h>
using namespace std;
static HANDLE semOne;
static HANDLE semTwo;
static int num;
/*
* 信号资源semOne初始为0,最大1个资源可用
* 信号资源semTwo初始为1,最大1个资源可用
*/
unsigned WINAPI Read(void* arg) {
int i;
for (i = 0; i < 5; i++) {
fputs("Input num:\n", stdout);
printf("begin read\n");
WaitForSingleObject(semTwo, INFINITE);
printf("beginning read\n");
scanf("%d", &num);
ReleaseSemaphore(semOne, 1, NULL);
}
return 0;
}
unsigned WINAPI Accu(void* arg) {
int sum = 0, i;
for (i = 0; i < 5; ++i) {
printf("begin Accu\n");
WaitForSingleObject(semOne, INFINITE);
printf("beginning Accu\n");
sum += num;
printf("sum=%d\n", sum);
ReleaseSemaphore(semTwo, 1, NULL);
}
return 0;
}
int main() {
HANDLE hThread1, hThread2;
semOne = CreateSemaphore(NULL, 0, 1, NULL);//初始值没有可用资源
semTwo = CreateSemaphore(NULL, 1, 1, NULL);//初始值有一个可用资源
hThread1 = (HANDLE)_beginthreadex(NULL, 0, Read, NULL, 0, NULL);
hThread2 = (HANDLE)_beginthreadex(NULL, 0, Accu, NULL, 0, NULL);
WaitForSingleObject(hThread1, INFINITE);
WaitForSingleObject(hThread2, INFINITE);
CloseHandle(semOne);
CloseHandle(semTwo);
system("pause");
return 0;
}
运行结果
文章评论