• 线程同步之信号量


    什么是信号量 

        信号量(semaphore)是操作系统用来解决并发中的互斥和同步问题的一种方法。与互斥量不同的地方是,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。

        信号量的工作原理以一个停车场为例,假设停车场只有三个车位,那么同一时刻最多只能有三辆车进入,还有其他车来时则必须在入口等待。只有当有一辆车离开停车场,才能允许其他车辆进入,如此往复。这个停车系统中,每辆车就好比一个线程,空车位数量就好比一个信号量,空车位数量限制了可以活动的线程。假如里面依然是三个车位,但是现在改变了规则,要求每次只能停两辆车,那么一开始进入两辆车,后面得等到有车离开才能有车进入,但是得保证最多停两辆车。对于Semaphore而言,就如同一个空车位数量,限制了可活动的线程数

    信号量的组成

    1. 计数器:该内核对象被使用的次数
    2. 最大资源数量:标识信号量可以控制的最大资源数量(带符号的32位)
    3. 当前资源数量:标识当前可用资源的数量(带符号的32位)。即表示当前开放资源的个数(注意不是剩下资源的个数),只有开放的资源才能被线程所申请。但这些开放的资源不一定被线程占用完。比如,当前开放5个资源,而只有3个线程申请,则还有2个资源可被申请,但如果这时总共是7个线程要使用信号量,显然开放的资源5个是不够的。这时还可以再开放2个,直到达到最大资源数量。

    信号量的规则

    信号量的规则如下:

    (1)如果当前资源计数大于0,那么信号量处于触发状态(有信号状态),表示有可用资源。

    (2)如果当前资源计数等于0,那么信号量属于未触发状态(无信号状态),表示没有可用资源。

    (3)系统绝对不会让当前资源计数变为负数

    (4)当前资源计数绝对不会大于最大资源计数

    创建信号量

    1. HANDLE WINAPI
    2. CreateSemaphoreW(
    3. _In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // Null 安全属性
    4. _In_ LONG lInitialCount, //初始化时,共有多少个资源是可以用的。 0:未触发状//态(无信号状态),表示没有可用资源
    5. _In_ LONG lMaximumCount, //能够处理的最大的资源数量 3
    6. _In_opt_ LPCWSTR lpName //NULL 信号量的名称
    7. );
    1. 第一个参数表示安全属性,这是创建内核对象函数都会有的参数,NULL表示默认安全属性
    2. 第二个参数表示初始时有多少个资源可用,0表示无任何资源(未触发状态)
    3. 第三个参数表示最大资源数
    4. 第四个参数表示信号量的名称,NULL表示无名称的信号量对象

     增加/释放信号量

    1. ReleaseSemaphore(
    2. _In_ HANDLE hSemaphore, //信号量的句柄
    3. _In_ LONG lReleaseCount, //将lReleaseCount值加到信号量的当前资源计数上面 0-> 1
    4. _Out_opt_ LPLONG lpPreviousCount //当前资源计数的原始值
    5. );
    1. 第一个参数表示信号量句柄,也就是调用创建信号量函数时返回的句柄
    2. 第二个参数表示释放的信号量个数,该值必须大于0,但不能大于信号量的最大计数
    3. 第三个参数表示指向要接收信号量的上一个计数的变量的指针。如果不需要上一个计数, 则此参数可以为NULL 。

    关闭句柄

    1. CloseHandle(
    2. _In_ _Post_ptr_invalid_ HANDLE hObject
    3. );

    下面是一个简单程序示例:

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. int main() {
    6. HANDLE semaphore;
    7. semaphore = CreateSemaphore(NULL, 0, 1, NULL);
    8. CloseHandle(semaphore);
    9. return 0;
    10. }

    上面这个程序创建了一个初始时0个资源,最大资源数为1的信号量。必须等待ReleaseSemaphore增加信号量后才能被使用。这种最大资源为1的信号量相当于一种特殊的互斥信号量。

    资源信号量示例 

    下面这段程序创建了两个信号资源,其最大资源都为1;一个初始资源为0,另一个初始资源为1。线程中的for循环每执行一次会将另一个要申请的信号资源的可用资源数+1。因此程序的执行结果为两个线程中的for循环交替执行。

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. static HANDLE semOne;
    6. static HANDLE semTwo;
    7. static int num;
    8. /*
    9. * 信号资源semOne初始为0,最大1个资源可用
    10. * 信号资源semTwo初始为1,最大1个资源可用
    11. */
    12. unsigned WINAPI Read(void* arg) {
    13. int i;
    14. for (i = 0; i < 5; i++) {
    15. fputs("Input num:\n", stdout);
    16. printf("begin read\n");
    17. WaitForSingleObject(semTwo, INFINITE);
    18. printf("beginning read\n");
    19. scanf("%d", &num);
    20. ReleaseSemaphore(semOne, 1, NULL);
    21. }
    22. return 0;
    23. }
    24. unsigned WINAPI Accu(void* arg) {
    25. int sum = 0, i;
    26. for (i = 0; i < 5; ++i) {
    27. printf("begin Accu\n");
    28. WaitForSingleObject(semOne, INFINITE);
    29. printf("beginning Accu\n");
    30. sum += num;
    31. printf("sum=%d\n", sum);
    32. ReleaseSemaphore(semTwo, 1, NULL);
    33. }
    34. return 0;
    35. }
    36. int main() {
    37. HANDLE hThread1, hThread2;
    38. semOne = CreateSemaphore(NULL, 0, 1, NULL);//初始值没有可用资源
    39. semTwo = CreateSemaphore(NULL, 1, 1, NULL);//初始值有一个可用资源
    40. hThread1 = (HANDLE)_beginthreadex(NULL, 0, Read, NULL, 0, NULL);
    41. hThread2 = (HANDLE)_beginthreadex(NULL, 0, Accu, NULL, 0, NULL);
    42. WaitForSingleObject(hThread1, INFINITE);
    43. WaitForSingleObject(hThread2, INFINITE);
    44. CloseHandle(semOne);
    45. CloseHandle(semTwo);
    46. system("pause");
    47. return 0;
    48. }

    运行结果 

  • 相关阅读:
    史上最全,最详细SQL基础
    Some Multicast Commands on Huawei SW
    Java第2章 类与对象(一)
    最新版校园招聘进大厂系列----------(5)百度篇 -----未完待续
    协同过滤算法
    Docker 安装 Jenkins (保姆级图文教学)
    第十六章总结:反射和注解
    麒麟V10下离线部署Rancher无法打开监控
    最新版k8s 1.25版本安装
    『现学现忘』Git分支 — 41、分支基本操作(二)
  • 原文地址:https://blog.csdn.net/qq_54169998/article/details/127779401