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-基础:信号量的更多相关文章

  1. CPP基础

    CPP基础1. 如果没有指明访问限定符(public,private),class中默认的private,而struct中的成员默认是public的. #include <iostream> ...

  2. 个人学习记录-Cpp基础-成员初始化列表

    Translator     Translator     参考链接: https://blog.csdn.net/XIONGXING_xx/article/details/115553291http ...

  3. IOTutility 一个轻量级的 IOT 基础操作库

    IOTutility 一个轻量级的 IOT 基础操作库 Base utility for IOT devices, networking, controls etc... IOTutility 的目的 ...

  4. Redis计数信号量

    计数信号量是一种锁,它可以让用户限制一项资源最多能够同时被多少个进程访问,通常用于限定能够同时使用的资源数量.你可以把Redis分布式锁里面创建的锁看作是只能被一个进程访问的信号量. 计数信号量和其他 ...

  5. win7 32 bit VS2012 OpenCV3.0配置

    今天看CPP基础,想起来之前在vs2012配置opencv3未成功,就忍不住再次配置一... 环境:win7 32bit vs2012 opencv3.0 主要参考这几篇博文:1,2,3 上面的博文已 ...

  6. 在vs2013下手把手创建/调用dll

    body { font: 16px } 参考了大佬的文章 首先,体会一下静态编译: 创建Win32Project,选DLL,添加一个.h和.cpp文件 点击生成解决方案,然后去debug目录下拷贝.l ...

  7. 双目相机标定以及立体测距原理及OpenCV实现

    单目相机标定的目标是获取相机的内参和外参,内参(1/dx,1/dy,Cx,Cy,f)表征了相机的内部结构参数,外参是相机的旋转矩阵R和平移向量t.内参中dx和dy是相机单个感光单元芯片的长度和宽度,是 ...

  8. 头部姿态估计 - OpenCV/Dlib/Ceres

    基本思想 通过Dlib获得当前人脸的特征点,然后通过旋转平移标准模型的特征点进行拟合,计算标准模型求得的特征点与Dlib获得的特征点之间的差,使用Ceres不断迭代优化,最终得到最佳的旋转和平移参数. ...

  9. QT_study

    https://blog.csdn.net/a313827758/article/details/72736552 https://blog.csdn.net/xbcreal/article/deta ...

  10. 【Redis3.0.x】实战案例

    Redis3.0.x 实战案例 简介 <Redis实战>的学习笔记和总结. 书籍链接 初识 Redis Redis 简介 Redis 是一个速度非常快的键值对存储数据库,它可以存储键和五种 ...

随机推荐

  1. 提交代码至coding.net

    1.首先在本地任意目录下创建项目 2.cmd切换至该目录下,然后依次输入: git init git add . git commit -m "version 3.9" git r ...

  2. hdu 4003 Find Metal Mineral 树形dp ,*****

    Find Metal Mineral Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Other ...

  3. mysql case when的使用

    SELECT (CASE payType WHEN 1 THEN '微信' WHEN 2 THEN '支付宝' ELSE '余额' END) as type, count(payType) FROM ...

  4. js数组详解

        1,什么是数组 数组是值得有序集合,每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引.js的数组是无类型的,数组元素可以是任意类型,同一个数组中的不同元素可能是对象或数组 ...

  5. ccf-201609-2 火车购票

    问题描述 请实现一个铁路购票系统的简单座位分配算法,来处理一节车厢的座位分配. 假设一节车厢有20排.每一排5个座位.为方便起见,我们用1到100来给所有的座位编号,第一排是1到5号,第二排是6到10 ...

  6. 11.7NOIP模拟赛解题报告

    心路历程 预计得分:\(50 + 100 + 100\) 实际得分:\(50 + 100 +100\) T2 T3两道数据结构题美滋滋,然而写完就过去\(3h\)美滋滋 T1数学题学弟们都会做Orzz ...

  7. JS_1

    学习JS分为哪几步: 1.学习基础语法 JS写在哪 JS输出 JS变量 JS函数 JS分支 JS循环 2.学习JS操作网页DOM树 获取Dom节点 触发Dom事件 对Dom进行修改 3.学习JS对象及 ...

  8. 在WinServer上安装小红伞杀毒软件的经验总结

    作者:朱金灿 来源:http://blog.csdn.net/clever101 在WinServer2008或WinServer2012不能直接安装小红伞杀毒软件的免费版,需要安装服务器版.我手头并 ...

  9. <Android Framework 之路>BootAnimation(1)

    介绍 开机动画,BootAnimation,就是Android手机开机郭晨各种以一个展示给用户的界面,实际是一个多个帧组成的动画,在界面上进行一帧一帧的播放,形成开机动画的效果. 本文针对Androi ...

  10. 完整SQL分页存储过程(支持多表联接)

    http://www.cnblogs.com/andiki/archive/2009/03/24/1420289.html Code/********************************* ...