CPP-基础:信号量
windows api 多线程---信号量
信号量(Semaphore)和互斥量一样,属于内核对象。它自动执行可用资源查询的测试,如果有可用资源,则可用资源的计数减少1,从而避免其它线程请求资源。当该线程释放该资源后,可用资源计数增加1,则操作系统允许另一个线程请求资源。
信号量与临界区和互斥量的不同在于,它不属于某个线程。也就是说,一个线程可以等待信号量对象(减少它的资源计数),而另一个线程释放该对象(增加它的资源计数)。
图3 使用信号量对象控制资源
下面结合图例3来演示信号量对象对资源的控制。在图3中,以箭头和白色箭头表示共享资源所允许的最大资源计数和当前可用资源计数。初始如图(a)所示,最大资源计数和当前可用资源计数均为4,此后每增加一个对资源进行访问的线程(用黑色箭头表示)当前资源计数就会相应减1,图(b)即表示的在3个线程对共享资源进行访问时的状态。当进入线程数达到4个时,将如图(c)所示,此时已达到最大资源计数,而当前可用资源计数也已减到0,其他线程无法对共享资源进行访问。在当前占有资源的线程处理完毕而退出后,将会释放出空间,图(d)已有两个线程退出对资源的占有,当前可用计数为2,可以再允许2个线程进入到对资源的处理。可以看出,信号量是通过计数来对线程访问资源进行控制的,而实际上信号量确实也被称作Dijkstra计数器。
Win32 API提供了几个函数用于支持信号量。使用Win32 API产生一个信号量,必须首先调用CreateSemaphore()函数,该函数描述如下:
创建一个信号量
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName);
*@param lpSemaphoreAttributes 指定安全属性,如果是NULL就表示使用默认属性。
*@param lInitialCount 用于指定该信号量的初始资源计数,必须大于或等于0,并且小于或等于lMaximumCount。
*@param lMaximumCount 指定信号量的最大资源计数。
*@param lpName 是赋给信号量的字符串名字。
你可以根据信号量的字符串名字得到该信号量的句柄:
HANDLE OpenSemaphore(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName);
*@param dwDesiredAccess 访问方式,同互斥量参数。
*@param bInheritHandle 继承特性,同互斥量参数。
*@param lpName 信号量名字。
释放信号量函数:
BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount);
*@param hSemaphore 信号量的句柄。
*@param lReleaseCount指信号量现值的增额,该值必须大于0。
*@param lpPreviousCount 传回信号量原来的计数,可以为NULL。
释放信号量函数与释放互斥量函数形式相同,但不同之处在于:
一、任意线程可以在任意时刻调用此函数,因为信号量对象不为某个线程拥有。
二、ReleaseSemaphore()函数可以用大于1的大小来增加信号量的资源计数。
例1:
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#define threadnum 10
typedef struct THREADDATA
{
int id;
char name[];
int sleep;
}THREADDATA; HANDLE handleSemaphore;
char * str;
DWORD WINAPI ThreadProc( LPVOID lpParam )
{
THREADDATA *data=(THREADDATA *)lpParam; WaitForSingleObject(handleSemaphore,INFINITE);
for(int i=;i<;i++)
{
// WaitForSingleObject(handleSemaphore,INFINITE);
printf("thread%d:%d\n",data->id,i);
// ReleaseSemaphore(handleSemaphore,1,NULL);
Sleep(data->sleep);
}
ReleaseSemaphore(handleSemaphore,,NULL);
return ;
}
int main(int argc, char* argv[])
{
str=(char*)malloc();
THREADDATA pData[threadnum];
DWORD dwThreadId[threadnum];
HANDLE hThread[threadnum];
handleSemaphore=CreateSemaphore(NULL,,,"thread");
for(int i=;i<threadnum;i++)
{
pData[i].id=i;
sprintf(pData[i].name,"yuguoqing");
pData[i].sleep=i*;
hThread[i] = CreateThread(NULL,,ThreadProc, pData+i,, dwThreadId+i);
}
WaitForMultipleObjects(threadnum, hThread, TRUE, INFINITE);
return ;
}
信号量的使用特点使其更适用于对Socket(套接字)程序中线程的同步。例如,网络上的HTTP服务器要对同一时间内访问同一页面的用户数加以限制,这时可以为每个用户对服务器的页面请求设置一个线程,而页面则是待保护的共享资源,通过使用信号量对线程的同步作用可以确保在任一时刻无论有多少用户对某一页面进行访问,只有不大于设定的最大用户数目的线程能够进行访问,而其他的访问企图则被挂起,只有在有用户退出对此页面的访问后才有可能进入。下面给出的示例代码即展示了类似的处理过程:
// 信号量对象句柄
HANDLE hSemaphore;
UINT ThreadProc15(LPVOID pParam)
{
// 试图进入信号量关口
WaitForSingleObject(hSemaphore, INFINITE);
// 线程任务处理
AfxMessageBox("线程一正在执行!");
// 释放信号量计数
ReleaseSemaphore(hSemaphore, , NULL);
return ;
}
UINT ThreadProc16(LPVOID pParam)
{
// 试图进入信号量关口
WaitForSingleObject(hSemaphore, INFINITE);
// 线程任务处理
AfxMessageBox("线程二正在执行!");
// 释放信号量计数
ReleaseSemaphore(hSemaphore, , NULL);
return ;
}
UINT ThreadProc17(LPVOID pParam)
{
// 试图进入信号量关口
WaitForSingleObject(hSemaphore, INFINITE);
// 线程任务处理
AfxMessageBox("线程三正在执行!");
// 释放信号量计数
ReleaseSemaphore(hSemaphore, , NULL);
return ;
}
……
void CSample08View::OnSemaphore()
{
// 创建信号量对象
hSemaphore = CreateSemaphore(NULL, , , NULL);
// 开启线程
AfxBeginThread(ThreadProc15, NULL);
AfxBeginThread(ThreadProc16, NULL);
AfxBeginThread(ThreadProc17, NULL);
}
---------------------------------------

图4 开始进入的两个线程

图5 线程二退出后线程三才得以进入
上述代码在开启线程前首先创建了一个初始计数和最大资源计数均为2的信号量对象hSemaphore。即在同一时刻只允许2个线程进入由hSemaphore保护的共享资源。随后开启的三个线程均试图访问此共享资源,在前两个线程试图访问共享资源时,由于hSemaphore的当前可用资源计数分别为2和1,此时的hSemaphore是可以得到通知的,也就是说位于线程入口处的WaitForSingleObject()将立即返回,而在前两个线程进入到保护区域后,hSemaphore的当前资源计数减少到0,hSemaphore将不再得到通知,WaitForSingleObject()将线程挂起。直到此前进入到保护区的线程退出后才能得以进入。图4和图5为上述代脉的运行结果。从实验结果可以看出,信号量始终保持了同一时刻不超过2个线程的进入。
MFC中定义的信号量
在MFC中,通过CSemaphore类对信号量作了表述。该类只具有一个构造函数,可以构造一个信号量对象,并对初始资源计数、最大资源计数、对象名和安全属性等进行初始化,其原型如下:
CSemaphore( LONG lInitialCount = , LONG lMaxCount = , LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );
在构造了CSemaphore类对象后,任何一个访问受保护共享资源的线程都必须通过CSemaphore从父类CSyncObject类继承得到的Lock()和UnLock()成员函数来访问或释放CSemaphore对象。与前面介绍的几种通过MFC类保持线程同步的方法类似,通过CSemaphore类也可以将前面的线程同步代码进行改写,这两种使用信号量的线程同步方法无论是在实现原理上还是从实现结果上都是完全一致的。下面给出经MFC改写后的信号量线程同步代码:
// MFC信号量类对象
CSemaphore g_clsSemaphore(, );
UINT ThreadProc24(LPVOID pParam)
{
// 试图进入信号量关口
g_clsSemaphore.Lock();
// 线程任务处理
AfxMessageBox("线程一正在执行!");
// 释放信号量计数
g_clsSemaphore.Unlock();
return ;
}
UINT ThreadProc25(LPVOID pParam)
{
// 试图进入信号量关口
g_clsSemaphore.Lock();
// 线程任务处理
AfxMessageBox("线程二正在执行!");
// 释放信号量计数
g_clsSemaphore.Unlock();
return ;
}
UINT ThreadProc26(LPVOID pParam)
{
// 试图进入信号量关口
g_clsSemaphore.Lock();
// 线程任务处理
AfxMessageBox("线程三正在执行!");
// 释放信号量计数
g_clsSemaphore.Unlock();
return ;
}
……
void CSample08View::OnSemaphoreMfc()
{
// 开启线程
AfxBeginThread(ThreadProc24, NULL);
AfxBeginThread(ThreadProc25, NULL);
AfxBeginThread(ThreadProc26, NULL);
}
CPP-基础:信号量的更多相关文章
- CPP基础
CPP基础1. 如果没有指明访问限定符(public,private),class中默认的private,而struct中的成员默认是public的. #include <iostream> ...
- 个人学习记录-Cpp基础-成员初始化列表
Translator Translator 参考链接: https://blog.csdn.net/XIONGXING_xx/article/details/115553291http ...
- IOTutility 一个轻量级的 IOT 基础操作库
IOTutility 一个轻量级的 IOT 基础操作库 Base utility for IOT devices, networking, controls etc... IOTutility 的目的 ...
- Redis计数信号量
计数信号量是一种锁,它可以让用户限制一项资源最多能够同时被多少个进程访问,通常用于限定能够同时使用的资源数量.你可以把Redis分布式锁里面创建的锁看作是只能被一个进程访问的信号量. 计数信号量和其他 ...
- win7 32 bit VS2012 OpenCV3.0配置
今天看CPP基础,想起来之前在vs2012配置opencv3未成功,就忍不住再次配置一... 环境:win7 32bit vs2012 opencv3.0 主要参考这几篇博文:1,2,3 上面的博文已 ...
- 在vs2013下手把手创建/调用dll
body { font: 16px } 参考了大佬的文章 首先,体会一下静态编译: 创建Win32Project,选DLL,添加一个.h和.cpp文件 点击生成解决方案,然后去debug目录下拷贝.l ...
- 双目相机标定以及立体测距原理及OpenCV实现
单目相机标定的目标是获取相机的内参和外参,内参(1/dx,1/dy,Cx,Cy,f)表征了相机的内部结构参数,外参是相机的旋转矩阵R和平移向量t.内参中dx和dy是相机单个感光单元芯片的长度和宽度,是 ...
- 头部姿态估计 - OpenCV/Dlib/Ceres
基本思想 通过Dlib获得当前人脸的特征点,然后通过旋转平移标准模型的特征点进行拟合,计算标准模型求得的特征点与Dlib获得的特征点之间的差,使用Ceres不断迭代优化,最终得到最佳的旋转和平移参数. ...
- QT_study
https://blog.csdn.net/a313827758/article/details/72736552 https://blog.csdn.net/xbcreal/article/deta ...
- 【Redis3.0.x】实战案例
Redis3.0.x 实战案例 简介 <Redis实战>的学习笔记和总结. 书籍链接 初识 Redis Redis 简介 Redis 是一个速度非常快的键值对存储数据库,它可以存储键和五种 ...
随机推荐
- java泛型使用
泛型的解释 现在感觉泛型是一个值得学习的地方,就抽出时间来学习和总结一下泛型的使用. Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允 ...
- 紫书第5章 C++STL
例题 例题5-1 大理石在哪儿(Where is the Marble?,Uva 10474) 主要是熟悉一下sort和lower_bound的用法 关于lower_bound: http://blo ...
- shell编程之export
shell 与 export命令用户登录到Linux系统后,系统将启动一个用户shell.在这个shell中,可以使用shell命令 或声明变量,也可以创建并运行shell脚本程序.运行shell脚本 ...
- laravel JWT Auth - JSON Web令牌认证API
https://github.com/tymondesigns/jwt-auth/wiki
- Mybatis学习随笔
学习Mybatis路径(适合有java基础和mysql基础的小伙伴) 1.把项目搭建起来,跑一跑感受一下 2.测试基本映射 3.测试高级映射 4.测试动态sql 5.学习懒加载与缓存 6.与sprin ...
- css 样式表集合
说到前端不得不说一下css样式 css样式是用来装饰我们的html让整个页面显得更丰富多彩,所以我们要熟悉各种css样式,本人搜集了一下 供大家参考一下 字体属性:(font) 大小 {font-si ...
- cookie结合js 实现记住的拖拽
哈喽!!!我胡汉三又回来啦!!!有木有记挂挪啊!咱们今天说一个 cookie结合JS的小案例哦! 话不多说直接上代码: <!DOCTYPE html> <html> <h ...
- Linux基础之-元字符
Bash中的特殊字符,键盘上能敲出来的特殊字符都有其特殊意义,强调一点:元字符是被shell解释的. 1. '',取命令的执行结果 [root@MiWiFi-R3-srv ~]# ls4.txt an ...
- Oracle基础之Merge into
Merge into语句是Oracle9i新增的语法,用来合并UPDATE和INSERT语句. 通过MERGE语句,根据一张表或多表联合查询的连接条件对另外一张表进行查询,连接条件匹配上的进行UPDA ...
- jdk1.8 对数组及arrays类对数组的操作与增强
数组的初始化有两种方式 静态初始化: 初始化时由程序员显示置顶每个数组的初始值,由系统决定数组长度.如: int[] a1 = new int[] {1,2,3,4}; 动态初始化:初始化时由程序员只 ...