前面介绍了关键段CS事件Event互斥量Mutex在经典线程同步问题中的使用。本篇介绍用信号量Semaphore来解决这个问题。

首先也来看看如何使用信号量,信号量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. #include <stdio.h>
  2. #include <process.h>
  3. #include <windows.h>
  4. long g_nNum;
  5. unsigned int __stdcall Fun(void *pPM);
  6. const int THREAD_NUM = 10;
  7. //信号量与关键段
  8. HANDLE            g_hThreadParameter;
  9. CRITICAL_SECTION  g_csThreadCode;
  10. int main()
  11. {
  12. printf("     经典线程同步 信号量Semaphore\n");
  13. printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
  14. //初始化信号量和关键段
  15. g_hThreadParameter = CreateSemaphore(NULL, 0, 1, NULL);//当前0个资源,最大允许1个同时访问
  16. InitializeCriticalSection(&g_csThreadCode);
  17. HANDLE  handle[THREAD_NUM];
  18. g_nNum = 0;
  19. int i = 0;
  20. while (i < THREAD_NUM)
  21. {
  22. handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
  23. WaitForSingleObject(g_hThreadParameter, INFINITE);//等待信号量>0
  24. ++i;
  25. }
  26. WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
  27. //销毁信号量和关键段
  28. DeleteCriticalSection(&g_csThreadCode);
  29. CloseHandle(g_hThreadParameter);
  30. for (i = 0; i < THREAD_NUM; i++)
  31. CloseHandle(handle[i]);
  32. return 0;
  33. }
  34. unsigned int __stdcall Fun(void *pPM)
  35. {
  36. int nThreadNum = *(int *)pPM;
  37. ReleaseSemaphore(g_hThreadParameter, 1, NULL);//信号量++
  38. Sleep(50);//some work should to do
  39. EnterCriticalSection(&g_csThreadCode);
  40. ++g_nNum;
  41. Sleep(0);//some work should to do
  42. printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_nNum);
  43. LeaveCriticalSection(&g_csThreadCode);
  44. return 0;
  45. }

运行结果如下图:

可以看出来,信号量也可以解决线程之间的同步问题。

由于信号量可以计算资源当前剩余量并根据当前剩余量与零比较来决定信号量是处于触发状态或是未触发状态,因此信号量的应用范围相当广泛。本系列的第十篇将再次使用它来解决线程同步问题,欢迎大家参阅。

至此,经典线程同步问题全部结束了,下一篇将会对其作个总结以梳理各知识点。

多线程面试题系列(8):经典线程同步 信号量Semaphore的更多相关文章

  1. 秒杀多线程第八篇 经典线程同步 信号量Semaphore

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <且不超过最大资源数量. 第三个參数能够用来传出先前的资源计数,设为NULL表示不须要传出. 注意:当 ...

  2. 转---秒杀多线程第八篇 经典线程同步 信号量Semaphore

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  3. 经典线程同步 信号量Semaphore

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  4. 秒杀多线程第八篇 经典线程同步 信号量Semaphore (续)

    java semaphore实现: Semaphore当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用.Java 并发库 的Semaphore 可以很轻松完成信号 ...

  5. linux系统编程:线程同步-信号量(semaphore)

    线程同步-信号量(semaphore) 生产者与消费者问题再思考 在实际生活中,仅仅要有商品.消费者就能够消费,这没问题. 但生产者的生产并非无限的.比如,仓库是有限的,原材料是有限的,生产指标受消费 ...

  6. 秒杀多线程第六篇 经典线程同步 事件Event

    原文地址:http://blog.csdn.net/morewindows/article/details/7445233 上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权” ...

  7. 转--- 秒杀多线程第六篇 经典线程同步 事件Event

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题 ...

  8. 经典线程同步 互斥量Mutex

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  9. 经典线程同步 事件Event

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题 ...

随机推荐

  1. Vue动态组件

    前面的话 让多个组件使用同一个挂载点,并动态切换,这就是动态组件.本文将详细介绍Vue动态组件 概述 通过使用保留的 <component> 元素,动态地绑定到它的 is 特性,可以实现动 ...

  2. 腾讯织云:DevOps 流水线应用平台践行之路

    欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:梁定安,腾讯织云负责人,目前就职于腾讯社交网络运营部,任运维技术总监,开放运维联盟委员,腾讯云布道师,腾讯课堂运维讲师,EXIN D ...

  3. Clang之词法分析Lex

    Clang是LLVM编译器框架的前端(Frontend)编译器,可编译链接C.C++.Objective-C和Objective-C++四种语言的项目代码.Clang 的开发目标是提供一个可以替代 G ...

  4. SQL查询四舍五入 解决方法

    方法1: SELECT CAST('123.456' as decimal) 将会得到 123(小数点后面的将会被省略掉). 如果希望得到小数点后面的两位. 则需要把上面的改为 SELECT CAST ...

  5. Struts2学习笔记(一)——环境搭建

    1.创建Web项目并导入Struts2的主要jar包 在MyEclipse中新建Web项目,然后在lib目录下添加必须的jar包: 2.创建jsp页面 1).创建test.jsp页面: <bod ...

  6. SQLite在C#中的安装与操作

    SQLite 介绍 SQLite,是一款轻型的数据库,用于本地的数据储存. 先说说优点,它占用资源非常的低,在嵌入式设备中需要几百K的内存就够了:作为轻量级数据库,他的处理速度也足够快:支持的的容量级 ...

  7. STL之关联容器---set, mutilset, map, mutilmap

    STL的容器分为序列容器和关联容器.它们所表达的数据结构各有不同: 序列容器:vector(变长数组), list(链表), queue(队列), heap(堆算法)等 关联容器:set/mutils ...

  8. 【译】StackExchange.Redis中文使用文档

    StackExchange.Redis中文使用文档 Intro 最近想深入学习一些 Redis 相关的东西.于是看了看官方的项目StackExchange.Redis,发现里面有一份文档,于是打算翻译 ...

  9. 现在开始学习WPF了,mongodb在整理一下

    回忆一下自己学习mongodb的过程 1安装 2增删改查 3数据类型转换 4GridFS 5权限管理--开启权限之前先建立一个超级用户(admin库中),开启权限,用该用户登陆,进入admin数据库( ...

  10. if处理多分支结构

    实例1 import java.util.Scanner; /** * Created by liwenj on 2017/7/17. */ public class test7 { public s ...