多线程(六)多线程同步_SemaPhore信号量
信号量依然是一种内核同步对象,它的作用在于控制共享资源的最大访问数量
例如:我们有一个服务器,为这服务器创建一个线程池,线程池有五个线程,每个线程处理1个请求。当五个线程都在处理请求时,这个线程池己到达使用上限,
可使用数量为0,无法再处理其它请求。此时又有新的请求到来,新的请求将被放入缓存中进行等待。当有线程处理完请求退出时,线程池可使用数量+1。
期间线程池会一直判断可使用数量是否大于0并且小于最大使用数量5?如果为真,会查找缓存中是否有等待的请求,如果有就取出一个请求进行处理。
整个线程池周而复始执行相应操作,始终保持着同时只能处理五个请求。
包含三个部份:
使用计数器:所有内存对象都有这个属性
最大资源计数器: 控制所能访问资源的最大数量
当前资源计数器: 当前可以使用的资源数量
注意:当前资源计数永远不可能大于最大资源计数,也不会是个负数。当前资源计数为0时,信号量是未触发状态。当前资源计数大于0时,信号是触发状态
线程中调用等待函数时,等待函数内部会检查当前资源计数器,如果发现当前资源计数器为0是未触发状态,线程就会处于等待状态。
如果发现当前资源计数器>0,就会把资源计数器-1,然后使当前线程变为可调度状态。线程执行完相关代码后需要调用ReleaseSemphore增加当前可以使用的资源数量
创建信号对象:
HANDLE CreateSemaphore(PSECURITY_ATTRIBUTE psa,LONG lInitialCount,LONG lMaximumCount,PCTSTR pszName);
1.psa 指向安全属性指针,一般为NULL
2. lInitialCount 初始时有多少资源可供使用,一般=lMaximumCount
3. lMaximumCount 所能访问资源的最大数量
4.pszName 信号对象名称,如果不跨进程使用,一般为NULL
返回值:
成功返回新信号对象句柄,失败返回0
释放信号对象:增加当前可以使用的资源数量
BOOL ReleaseSemaphore(HANDLE hsem, LONG lReleaseCount,PLONG plPreviousCount);
1.hsem 信号对象句柄
2.lReleaseCount 改变的计数值,一般为1,当前资源计数器会+1
3.plPreviousCount 返回当前资源数量的原始值,一般为NULL
编写一个Demo用于演示SemaPhore基本操作
功能介绍:
模拟公司员工上网系统,一共有10名员工,同时只允许3名员工上网,每人上网时间为5秒
开始编写代码:
1. 创建个基于对话框的工程SemaPhoreDemo
2. 添加一个富文本框用于显示信息,修改属性支持多行,并且为只读. 然后绑定一个变量
3. 定义相关全局变量和线程函数前置声明
// CSemaPhoreDlg 对话框
#define WM_UPDATAEDIT WM_USER+100 //自定义消息用来更新编辑框 HANDLE g_hSemaPhore; //信号对象句柄
HWND g_hwndMain; //当前窗口句柄 //线程函数前置声明
DWORD WINAPI Thread_One(LPVOID lpParam);
DWORD WINAPI Thread_Two(LPVOID lpParam);
DWORD WINAPI Thread_Three(LPVOID lpParam);
DWORD WINAPI Thread_Four(LPVOID lpParam);
DWORD WINAPI Thread_Five(LPVOID lpParam);
DWORD WINAPI Thread_Six(LPVOID lpParam);
DWORD WINAPI Thread_Seven(LPVOID lpParam);
DWORD WINAPI Thread_Eight(LPVOID lpParam);
DWORD WINAPI Thread_Nine(LPVOID lpParam);
DWORD WINAPI Thread_Ten(LPVOID lpParam);
相关全局变量和线程函数的前置声明
4.窗口添加个消息响应_用于更新编辑框信息
afx_msg LRESULT CSemaPhoreDlg::OnUpdataedit(WPARAM wParam, LPARAM lParam)
{ m_richEdit.UpdateData(TRUE);
CString str;
m_richEdit.GetWindowText(str);
str += (LPCTSTR)wParam;
str += _T("\r");
m_richEdit.SetWindowText(str); //开始检测每一行文本,只要有"结束"字符串就把该行用红色显示
CHARFORMAT cf;
ZeroMemory(&cf, sizeof(CHARFORMAT));
cf.cbSize = sizeof(CHARFORMAT);
cf.dwMask = CFM_COLOR;
cf.crTextColor = RGB(, , ); CString strLine;
for (int i = ; i<m_richEdit.GetLineCount(); i++)
{
m_richEdit.GetLine(i,strLine.GetBufferSetLength(),);
strLine.ReleaseBuffer();
int nIndex = strLine.Find(_T("结束"));
if(nIndex!=-)
{
int lineStart = m_richEdit.LineIndex(i);//取当前行第一个字符的索引
int lineEnd = lineStart + nIndex + ;
m_richEdit.SetSel(lineStart, lineEnd);//选取第一行字符
m_richEdit.SetSelectionCharFormat(cf);//设置颜色 }
} m_richEdit.SetSel(-,-);
m_richEdit.UpdateData(FALSE); return ;
}
消息响应_用于更新编辑框信息
5.OnInitDialog添加相关代码
BOOL CSemaPhoreDlg::OnInitDialog()
{
CDialogEx::OnInitDialog(); // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标 //创建信号对象,初始和最大使用数量都为10
g_hSemaPhore = CreateSemaphore(NULL,,,NULL);
//获取主窗口句柄,供线程内部发送消息用以更新编辑框
g_hwndMain = this->m_hWnd;
//创建十个员工线程
CloseHandle(CreateThread(NULL,,Thread_One,NULL,,NULL));
CloseHandle(CreateThread(NULL,,Thread_Two,NULL,,NULL));
CloseHandle(CreateThread(NULL,,Thread_Three,NULL,,NULL));
CloseHandle(CreateThread(NULL,,Thread_Four,NULL,,NULL));
CloseHandle(CreateThread(NULL,,Thread_Five,NULL,,NULL));
CloseHandle(CreateThread(NULL,,Thread_Six,NULL,,NULL));
CloseHandle(CreateThread(NULL,,Thread_Seven,NULL,,NULL));
CloseHandle(CreateThread(NULL,,Thread_Eight,NULL,,NULL));
CloseHandle(CreateThread(NULL,,Thread_Nine,NULL,,NULL));
CloseHandle(CreateThread(NULL,,Thread_Ten,NULL,,NULL)); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
OnInitDialog
6.十个员工线程函数
//线程_员工1
DWORD WINAPI Thread_One(LPVOID lpParam)
{
WaitForSingleObject(g_hSemaPhore,INFINITE); SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工1:开始上网"),NULL);
//沿时3秒
Sleep();
SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工1:上网结束"),NULL); //增加当前可使用计数
ReleaseSemaphore(g_hSemaPhore,,NULL); return TRUE;
}
//线程_员工2
DWORD WINAPI Thread_Two(LPVOID lpParam)
{
WaitForSingleObject(g_hSemaPhore,INFINITE); SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工2:开始上网"),NULL);
//沿时3秒
Sleep();
SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工2:上网结束"),NULL); //增加当前可使用计数
ReleaseSemaphore(g_hSemaPhore,,NULL); return TRUE;
}
//线程_员工3
DWORD WINAPI Thread_Three(LPVOID lpParam)
{
WaitForSingleObject(g_hSemaPhore,INFINITE); SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工3:开始上网"),NULL);
//沿时3秒
Sleep();
SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工3:上网结束"),NULL); //增加当前可使用计数
ReleaseSemaphore(g_hSemaPhore,,NULL); return TRUE;
}
//线程_员工4
DWORD WINAPI Thread_Four(LPVOID lpParam)
{
WaitForSingleObject(g_hSemaPhore,INFINITE); SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工4:开始上网"),NULL);
//沿时3秒
Sleep();
SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工4:上网结束"),NULL); //增加当前可使用计数
ReleaseSemaphore(g_hSemaPhore,,NULL); return TRUE;
}
//线程_员工5
DWORD WINAPI Thread_Five(LPVOID lpParam)
{
WaitForSingleObject(g_hSemaPhore,INFINITE); SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工5:开始上网"),NULL);
//沿时3秒
Sleep();
SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工5:上网结束"),NULL); //增加当前可使用计数
ReleaseSemaphore(g_hSemaPhore,,NULL); return TRUE;
}
//线程_员工6
DWORD WINAPI Thread_Six(LPVOID lpParam)
{
WaitForSingleObject(g_hSemaPhore,INFINITE); SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工6:开始上网"),NULL);
//沿时3秒
Sleep();
SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工6:上网结束"),NULL); //增加当前可使用计数
ReleaseSemaphore(g_hSemaPhore,,NULL); return TRUE;
}
//线程_员工7
DWORD WINAPI Thread_Seven(LPVOID lpParam)
{
WaitForSingleObject(g_hSemaPhore,INFINITE); SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工7:开始上网"),NULL);
//沿时3秒
Sleep();
SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工7:上网结束"),NULL); //增加当前可使用计数
ReleaseSemaphore(g_hSemaPhore,,NULL); return TRUE;
}
//线程_员工8
DWORD WINAPI Thread_Eight(LPVOID lpParam)
{
WaitForSingleObject(g_hSemaPhore,INFINITE); SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工8:开始上网"),NULL);
//沿时3秒
Sleep();
SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工8:上网结束"),NULL); //增加当前可使用计数
ReleaseSemaphore(g_hSemaPhore,,NULL); return TRUE;
}
//线程_员工9
DWORD WINAPI Thread_Nine(LPVOID lpParam)
{
WaitForSingleObject(g_hSemaPhore,INFINITE); SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工9:开始上网"),NULL);
//沿时3秒
Sleep();
SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工9:上网结束"),NULL); //增加当前可使用计数
ReleaseSemaphore(g_hSemaPhore,,NULL); return TRUE;
}
//线程_员工10
DWORD WINAPI Thread_Ten(LPVOID lpParam)
{
WaitForSingleObject(g_hSemaPhore,INFINITE); SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工10:开始上网"),NULL);
//沿时3秒
Sleep();
SendMessage(g_hwndMain,WM_UPDATAEDIT,(WPARAM)_T("线程_员工10:上网结束"),NULL); //增加当前可使用计数
ReleaseSemaphore(g_hSemaPhore,,NULL); return TRUE;
}
十个员工线程函数
7.DestroyWindow
BOOL CSemaPhoreDlg::DestroyWindow()
{
CloseHandle(g_hSemaPhore); return CDialogEx::DestroyWindow();
}
DestroyWindow
最终演示效果如下:

分析下整个执行流程:
最大访问数量=3, 当前可访问资源数量=3
线程_员工2:开始上网 //当前可访问资源数量=2
线程_员工1:开始上网 //当前可访问资源数量=1
线程_员工3:开始上网 //当前可访问资源数量=0 为0此时信号量对象为未触发状态,所有等待该信号量的线程都处于等待
线程_员工2:上网结束 //当前可访问资源数量=1 不为0时,此时信号量对象为触发状态,等待的线程中有1个线程可以苏醒
线程_员工3:上网结束 //当前可访问资源数量=2 此时有2个线程可以苏醒
线程_员工1:上网结束 //当前可访问资源数量=3 此时有3个线程可以苏醒
线程_员工4:开始上网 //当前可访问资源数量=2 此时还有2个线程可以苏醒
线程_员工5:开始上网 //当前可访问资源数量=1 此时还有1个线程可以苏醒
线程_员工6:开始上网 //当前可访问资源数量=0 为0此时信号量对象为未触发状态,所有等待该信号量的线程都处于等待
线程_员工4:上网结束 //当前可访问资源数量=1 不为0时,此时信号量对象为触发状态,等待的线程中有1个线程可以苏醒
线程_员工5:上网结束 //当前可访问资源数量=2 此时有2个线程可以苏醒
线程_员工7:开始上网 //当前可访问资源数量=1 此时还有1个线程可以苏醒
线程_员工6:上网结束 //当前可访问资源数量=2 此时有2个线程可以苏醒
线程_员工8:开始上网 //当前可访问资源数量=1 此时还有1个线程可以苏醒
线程_员工9:开始上网 //当前可访问资源数量=0 为0此时信号量对象为未触发状态,所有等待该信号量的线程都处于等待
线程_员工7:上网结束 //当前可访问资源数量=1 不为0时,此时信号量对象为触发状态,等待的线程中有1个线程可以苏醒
线程_员工10:开始上网 //当前可访问资源数量=0 为0此时信号量对象为未触发状态,所有等待该信号量的线程都处于等待
线程_员工8:上网结束 //当前可访问资源数量=1 不为0时,此时信号量对象为触发状态,等待的线程中有1个线程可以苏醒
线程_员工9:上网结束 //当前可访问资源数量=2 此时有2个线程可以苏醒
线程_员工10:上网结束 //当前可访问资源数量=2 此时有3个线程可以苏醒
从上面就可以看得出来,无论任何时候当前可访问资源数量都是>0 并且 小于 最大访问数量,并且始终最多只会有3条线程在运行,当有3条线程在执行时,其它线程就一直处于等待中
多线程(六)多线程同步_SemaPhore信号量的更多相关文章
- 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent
[源码下载] 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEve ...
- C# 多线程六之Task(任务)三之任务工厂
1.知识回顾,简要概述 前面两篇关于Task的随笔,C# 多线程五之Task(任务)一 和 C# 多线程六之Task(任务)二,介绍了关于Task的一些基本的用法,以及一些使用的要点,如果都看懂了,本 ...
- c#中多线程间的同步
目录 一.引入 二.Lock 三.Monitor 四.Interlocked 五.Semaphore 六.Event 七.Barrier 八.ReaderWriterLockSlim 九.Mutex ...
- C#多线程之线程同步篇2
在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作.使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造.Manual ...
- 数据结构(逻辑结构,物理结构,特点) C#多线程编程的同步也线程安全 C#多线程编程笔记 String 与 StringBuilder (StringBuffer) 数据结构与算法-初体验(极客专栏)
数据结构(逻辑结构,物理结构,特点) 一.数据的逻辑结构:指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后件关系,而与他们在计算机中的存储位置无关.逻辑结构包括: 集合 数 ...
- C# 多线程之线程同步
多线程间应尽量避免同步问题,最好不要线程间共享数据.如果必须要共享数据,就需要使用同步技术,确保一次只有一个线程访问和改变共享状态. 一::lock语句 lock语句事设置锁定和接触锁定的一种简单方法 ...
- 【2017-06-20】Linux应用开发工程师C/C++面试问题记录之一:Linux多线程程序的同步问题
参考之一:Linux 线程同步的三种方法 链接地址:http://www.cnblogs.com/eleclsc/p/5838790.html 简要回答: Linux下线程同步最常用的三种方法就是互斥 ...
- C#多线程之线程同步篇3
在上一篇C#多线程之线程同步篇2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier ...
- C#多线程之线程同步篇1
在多线程(线程同步)中,我们将学习多线程中操作共享资源的技术,学习到的知识点如下所示: 执行基本的原子操作 使用Mutex构造 使用SemaphoreSlim构造 使用AutoResetEvent构造 ...
随机推荐
- Internet Download Manager是什么?
在互联网下载管理器(也被称为IDM)是一款共享软件下载管理器,这意味着你可以下载该程序,并尝试它的试用期内免费. 以下是IDM最佳功能的列表: 支持多种浏览器和应用程序 一键下载文件 内置防病毒检查 ...
- CodeForces 1238C(思维+贪心)
题意 https://vjudge.net/problem/CodeForces-1238C 您现在正在玩一个游戏,您初始在一个高度 h 的悬崖 悬崖沿壁高度为 1-h 的这些位置均有平台,平台有两种 ...
- [LeetCode] 42. Trapping Rain Water 收集雨水
Given n non-negative integers representing an elevation map where the width of each bar is 1, comput ...
- HTML连载45-浮动元素脱标、排序规则、贴靠现象
一.浮动元素脱标 1.什么是浮动元素脱标 脱标:脱离标准流. 当某一个元素浮动之后,那么这个元素看上去就像被从标准流中删除了一样 2.浮动元素脱标之后有什么影响 如果前面一个元素浮动了,而后面的一个元 ...
- python学习第一天第二天总结
变量赋值 1, 变量由字⺟, 数字,下划线搭配组合⽽成 2, 不可以⽤数字开头,更不能是全数字 3,不能是pythond的关键字, 这些符号和字⺟已经被python占⽤, 不可以更改 4,不要⽤中⽂ ...
- javascript实现base64编码、解码
我们知道,浏览器的window对象提供有window.atob()和window.btoa()方法可以对字符串进行Base64编码和解码. console.log(window.btoa(window ...
- 黄聪:php一句代码让http跳转https
//其他框架请加到入口某共公加载的文件中 //方法一 https状态 if(!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off'){ Head ...
- CENTOS 7 升级内核版本(附带升级脚本)
写在前面的话 对于系统而言,除非是那种安全性要求非常高的公司或者经常会有第三方安全机构对其漏洞扫描的才容易涉及到系统的内核升级,比如之前呆过一个公司,因为需要做三级等保的原因,就会涉及到系统扫描,这时 ...
- StreamWriter StreamReader
private void WriteLoginJsonData(object jsonData) { using (FileStream writerFileStream = new FileStre ...
- 3 JDK并发包
JDK内部提供了大量实用的API和框架.本章主要介绍这些JDK内部功能,主要分为3大部分: 首先,介绍有关同步控制的工具,之前介绍的synchronized就是一种同步控制手段,将介绍更加丰富的多线程 ...