利用信号量实现线程同步
本篇使用信号量机制实现对全局资源的正确使用,包括以下两点:
- 各个子线程对全局资源的互斥使用
- 主线程对子线程的同步
信号量
简单的说,信号量内核对象,也是多线程同步的一种机制,它可以对资源访问进行计数,包括最大资源计数和当前资源计数,是两个32位的值;另外,计数是以原子访问的方式进行,由操作系统维护;
- 最大资源计数,表示可以控件的最大资源数量
- 当前资源计数,表示当前可用资源的数量
信号量的规则:
- 如果当前资源计数器大于0,那么信号量处于触发状态
- 如果当前资源计数器等于0,那么信号量处于未触发状态
- 系统绝对不会让当前资源计数器变为负数
- 当前资源计数器决定不会大于最大资源计数
信号量机制:
以一个停车场的运作为例。假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用,当当前资源计数大于0,信号量处于触发状态,线程编程可调度;
相关API
- 创建信号量
HANDLE WINAPI CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//more设置为NULLLONG lInitialCount, //当前可用资源的数量LONG lMaximumCount, //最大资源数量LPCTSTR lpName //信号量名字,可以设置为NULL
);
- 释放信号量
BOOL WINAPI ReleaseSemaphore(HANDLE hSemaphore, //信号量句柄LONG lReleaseCount, //该函数返回时,当前可用资源的数增加lReleaseCount个LPLONG lpPreviousCount
);
- 打开已经存在的信号量
HANDLE WINAPI OpenSemaphore(DWORD dwDesiredAccess,BOOL bInheritHandle,LPCTSTR lpName //打开信号量的名字
);
- wait函数
DWORD WINAPI WaitForSingleObject(HANDLE hHandle, //当等待成功时,当前可用信号量递减DWORD dwMilliseconds //等待时间
);
线程同步举例
线程同步包含两层含义:
- 对全局资源互斥访问
- 线程间的有序、协同执行(比如:线程A执行完后,再进行线程B的操作)
例子1 互斥访问
#include "stdafx.h"
#include <iostream>
#include <afxmt.h>
using namespace std;int g_nIndex = 0;
const int nMaxCnt = 20;#define MAX_SEM_COUNT 1
#define THREADCOUNT 12HANDLE ghSemaphore;DWORD WINAPI ThreadProcBySEM( LPVOID );int _tmain(int argc, _TCHAR* argv[])
{HANDLE aThread[THREADCOUNT];int i;// Create a semaphore with initial and max counts of MAX_SEM_COUNT// 备注:实现多个子线程资源互斥范围,最大信号量需要设置为1ghSemaphore = CreateSemaphore( NULL, // default security attributes1, // initial count1, // maximum countNULL); // unnamed semaphoreif (ghSemaphore == NULL) {printf("CreateSemaphore error: %d\n", GetLastError());return 0;}// Create worker threadsfor( i=0; i < THREADCOUNT; i++ ){aThread[i] = CreateThread( NULL, // default security attributes0, // default stack size(LPTHREAD_START_ROUTINE) ThreadProcBySEM, NULL, // no thread function arguments0, // default creation flagsNULL); // receive thread identifierif( aThread[i] == NULL ){printf("CreateThread error: %d\n", GetLastError());return 0;}}//Wait for all threads to terminateWaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);//Close thread and semaphore handlesfor( i=0; i < THREADCOUNT; i++ )CloseHandle(aThread[i]);CloseHandle(ghSemaphore);return 0;
}DWORD WINAPI ThreadProcBySEM(LPVOID lpParam)
{DWORD dwWaitResult;BOOL bContinue = TRUE;while(bContinue){//Try to enter the semaphore gate.dwWaitResult = WaitForSingleObject(ghSemaphore,INFINITE);if (WAIT_OBJECT_0 == dwWaitResult){if (g_nIndex++ < nMaxCnt){//Simulate thread spending time on taskSleep(5);printf("Thread %d: Index = %d\n", GetCurrentThreadId(), g_nIndex);}else{bContinue = FALSE;}// Relase the semaphore when task is finishedif (!ReleaseSemaphore( ghSemaphore, // handle to semaphore1, // increase count by oneNULL)) // not interested in previous count{printf("ReleaseSemaphore error: %d\n", GetLastError());}}else{break;}}return TRUE;
}
运行结果:
例子2 线程同步
下面我们将线程创建时的顺序编号,也打印出来,实现方式是将变量i传递给线程,以实现打印;
错误代码:
我们将编号i的地址作为CreateThread入参,传递给线程函数ThreadProcBySEM;主要修改如下:
// Create worker threadsfor( i=0; i < THREADCOUNT; i++ ){aThread[i] = CreateThread( NULL, // default security attributes0, // default stack size(LPTHREAD_START_ROUTINE) ThreadProcBySEM, &i, //线程编号地址0, // default creation flagsNULL); // receive thread identifierif( aThread[i] == NULL ){printf("CreateThread error: %d\n", GetLastError());return 0;}}//线程函数代码:
DWORD WINAPI ThreadProcBySEM(LPVOID lpParam)
{DWORD dwWaitResult;BOOL bContinue = TRUE;if (NULL == lpParam){return FALSE;}//获取编号,线程创建有时间开销,当函数执行到,赋值语句时//lpParam所指向的是变量i,但是主线程已经对i进行++,不再是创建时的值了int nThreadNum = *(int*)lpParam;if (WAIT_OBJECT_0 == dwWaitResult){if (g_nIndex++ < nMaxCnt){//Simulate thread spending time on taskSleep(5);//打印线程编号printf("NO %d = Thread %d: Index = %d\n", nThreadNum, GetCurrentThreadId(), g_nIndex);}}
}
运行结果:
从下图中可以看出,不同的线程句柄,却有相同的线程编号,属于异常线程;
原因分析:
线程创建到线程函数执行存在时间开销,线程函数不会第一时间开始执行,而此时主线程还处于非阻塞状态,主线程仍然可以对变量i不断进行++操作,导致当前线程进行访问时,其内容已经不再是当前那个值了;
正确做法:
1.引入关键代码段同步对象,用于各个子线间,访问全局资源g_nIndex,
2.在创建新线程前,需要将当前线程的编号保存到,临时变量;
3.将信号量对象用于主线程和子线程间的同步;
全部代码如下:
#include "stdafx.h"
//#include <windows.h>
#include <iostream>
#include <afxmt.h>
using namespace std;#define MAX_SEM_COUNT 1
#define THREADCOUNT 12int g_nIndex = 0;
const int nMaxCnt = 20;HANDLE ghSemaphore;
CRITICAL_SECTION g_csLockA;DWORD WINAPI ThreadProcBySEM( LPVOID );int _tmain(int argc, _TCHAR* argv[])
{HANDLE aThread[THREADCOUNT];int i;// Create a semaphore with initial and max counts of MAX_SEM_COUNTghSemaphore = CreateSemaphore( NULL, // default security attributes0, // initial count,modify:当前处于非触发状态1, // maximum countNULL); // unnamed semaphoreif (ghSemaphore == NULL) {printf("CreateSemaphore error: %d\n", GetLastError());return 0;}//初始化关键代码段对象,用于访问全局资源的互斥InitializeCriticalSection(&g_csLockA);// Create worker threadsfor( i=0; i < THREADCOUNT; i++ ){aThread[i] = CreateThread( NULL, // default security attributes0, // default stack size(LPTHREAD_START_ROUTINE) ThreadProcBySEM, &i, // no thread function arguments0, // default creation flagsNULL); // receive thread identifierif( aThread[i] == NULL ){printf("CreateThread error: %d\n", GetLastError());return 0;}//modify 创建新线程前,需要将编号保存起来,才创建新线程//用于同步主线程和子线程WaitForSingleObject(ghSemaphore,INFINITE);}//Wait for all threads to terminateWaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);//Close thread and semaphore handlesfor( i=0; i < THREADCOUNT; i++ )CloseHandle(aThread[i]);CloseHandle(ghSemaphore);DeleteCriticalSection(&g_csLockA);return 0;
}DWORD WINAPI ThreadProcBySEM(LPVOID lpParam)
{DWORD dwWaitResult;BOOL bContinue = TRUE;if (NULL == lpParam){return FALSE;}int nThreadNum = *(int*)lpParam;// Relase the semaphore if (!ReleaseSemaphore( ghSemaphore, // handle to semaphore1, // increase count by oneNULL)) // not interested in previous count{printf("ReleaseSemaphore error: %d\n", GetLastError());}while(bContinue){Sleep(10);//modify:当前线程处于休眠,给其他线程执行机会//Try to enter the cs gate. //modify:保护资源的唯一性访问EnterCriticalSection(&g_csLockA);if (g_nIndex++ < nMaxCnt){printf("NO %02d = Thread %d: Index = %d\n", nThreadNum, GetCurrentThreadId(), g_nIndex);}else{bContinue = FALSE;}LeaveCriticalSection(&g_csLockA);}return TRUE;
}
运行结果:
从运行结果看出,线程句柄和编号可以一一对应,并且资源访问也正常;
转载于:https://www.cnblogs.com/jinxiang1224/p/8468287.html
利用信号量实现线程同步相关推荐
- 实验三 使用POSIX信号量实现线程同步
实验三 使用POSIX信号量实现线程同步 目录 实验三 使用POSIX信号量实现线程同步 实验环境 一.实验目的 二.实验内容 三.实验步骤 四.实验总结 实验环境 操作系统版本:ubuntu-14. ...
- java中用关键字为共享资源加锁_利用synchronized实现线程同步的案例讲解
一.前期基础知识储备 (1)线程同步的定义:多线程之间的同步. (2)多线程同步原因:一个多线程的程序如果是通过Runnable接口实现的,则意味着类中的属性将被多个线程共享,由此引出资源的同步问题, ...
- mfc通过信号量保证线程同步
1.声明一个全局handle,记住在cpp里也声明 extern HANDLE uiHandle; 2.创建信号量 uiHandle = CreateSemaphore(NULL,1,1,NULL); ...
- C#并行编程(6):线程同步面面观
理解线程同步 线程的数据访问 在并行(多线程)环境中,不可避免地会存在多个线程同时访问某个数据的情况.多个线程对共享数据的访问有下面3种情形: 多个线程同时读取数据: 单个线程更新数据,此时其他线程读 ...
- 铂金04:令行禁止-为何说信号量是线程间的同步利器
欢迎来到<并发王者课>,本文是该系列文章中的第17篇. 在并发编程中,信号量是线程同步的重要工具.在本文中,我将带你认识信号量的概念.用法.种类以及Java中的信号量. 信号量(Semap ...
- linux线程基础篇----线程同步与互斥
linux线程基础----线程同步与互斥 一.同步的概念 1.同步概念 所谓同步,即同时起步,协调一致.不同的对象,对"同步"的理解方式略有不同.如,设备同步,是指在两个设备 ...
- 线程同步与异步套接字编程
1.利用事件对象来实现线程间的同步 新建一个win32 console application,取名Event,再建一个Event源文件,编辑: #include <iostream.h> ...
- 多线程怎么保证数据安全_Python threading实现多线程 提高篇 线程同步,以及各种锁...
本文主要讲多线程的线程之间的资源共享怎么保持同步. 多线程基础篇见,木头人:Python threading实现多线程 基础篇 Python的多线程,只有用于I/O密集型程序时效率才会有明显的提高,如 ...
- java 信号量 互斥锁_线程同步(互斥锁与信号量的作用与区别)
"信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里).而互斥锁是用在多线程多任务互斥的,一 ...
最新文章
- matlab-等高线图-三维曲线的绘制
- 此上下文中不支持函数定义。请在代码文件中创建函数。_深入解析Python上下文管理器,让你不再迷茫!...
- 数据结构算法 二进制转十进制_数据结构 - 栈
- 手工画设计模式的类图
- su封面插件_这届SU太优秀,一张纸建出一座音乐厅?
- python是什么类型的语言-python到底是什么类型的语言
- 字符串匹配算法知多少?
- 常数除以0的极限是什么_【高数总结求极限方法】百度作业帮
- linux终端ANSI转义字符
- 数据结构算法——1097. Hub Connection plan
- 在BREW中打造自己的GUI(8)-IWEB的封装
- 立创EDA导出Altium Designer的pcb文件没有没有显示飞线
- Graph Visualization and Navigation in Information Visualization: A Survey 译文
- Crawlab(crawlab github)
- 网易(weather)天气预报接口
- Android中播放本地SD卡中歌曲需要的添加的权限
- 算法刷题 -- 1823. 找出游戏的获胜者<难度 ★★☆>
- 不正确的c语言语句是,【单选题】下列不正确的C语言语句是( )。
A. x=y=5;
B. x=1,y=2;
C. y=int x;
D. x++;...
- 熔断器 java_SpringCloud之熔断器使用(Hystrix)
- 在抖音及一些直播上,如何进行违禁词在线检测呢?
热门文章
- Android 加入一个动作按钮
- 370万开发者,14万家企业!飞桨中国行落地深圳 激发AI软硬件创新发展新动能...
- 压缩之后神经网络忘记了什么?Google研究员给出了答案
- 速进!2000核实计算资源免费领取,名额有限,即开即送!
- 3D-BoNet:比3D点云实例分割算法快10倍!代码已开源
- Python知识点之Python面向对象
- easyx鼠标放置前按钮颜色_七种正确使用鼠标的好习惯,让你摆脱鼠标手的痛苦...
- 从V1到V4,让你读懂YOLO原理——深度AI科普团队
- 最先进单插槽专业绘图解决方案
- 基础知识(一)matlab与c++混合编程之环境搭建