多线程第六篇:信号量

2024-08-24 16:18

本文主要是介绍多线程第六篇:信号量,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

信号量及信号量上的操作是E.W.Dijkstra 在1965年提出的一种解决同步、互斥问题的较通用的方法,并在很多操作系统中得以实现, Linux改进并实现了这种机制。
信号量(semaphore )实际是一个整数,它的值由多个进程进行测试(test)和设置(set)。就每个进程所关心的测试和设置操作而言,这两个操作是不可中断的,或称“原子”操作,即一旦开始直到两个操作全部完成。测试和设置操作的结果是:信号量的当前值和设置值相加,其和或者是正或者为负。根据测试和设置操作的结果,一个进程可能必须睡眠,直到有另一个进程改变信号量的值。
信号量可用来实现所谓的“临界区”的互斥使用,临界区指同一时刻只能有一个进程执行其中代码的代码段。为了进一步理解信号量的使用,下面我们举例说明。
       假设你有很多相互协作的进程,它们正在读或写一个数据文件中的记录。你可能希望严格协调对这个文件的存取,于是你使用初始值为1的信号量,在这个信号量上实施两个操作,首先测试并且给信号量的值减1,然后测试并给信号量的值加1。当第一个进程存取文件时,它把信号量的值减1,并获得成功,信号量的值现在变为0,这个进程可以继续执行并存取数据文件。但是,如果另外一个进程也希望存取这个文件,那么它也把信号量的值减1,结果是不能存取这个文件,因为信号量的值变为-1。这个进程将被挂起,直到第一个进程完成对数据文件的存取。当第一个进程完成对数据文件的存取,它将增加信号量的值,使它重新变为1,现在,等待的进程被唤醒,它对信号量的减1操作将获得成功。
上述的进程互斥问题,是针对进程之间要共享一个临界资源而言的,信号量的初值为1。实际上,信号量作为资源计数器,它的初值可以是任何正整数,其初值不一定为01。另外,如果一个进程要先获得两个或多个的共享资源后才能执行的话,那么,相应地也需要多个信号量,而多个进程要分别获得多个临界资源后方能运行,这就是信号量集合机制.

首先也来看看如何使用信号量,信号量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()就可以完成清理与销毁了。

信号量其实就是人们常说的PV操作.p操作是计数器+1,即 ReleaseSemaphore函数,而v操作是-1,即waitforsignalobject操作.

我们先来一个简单的,用信号量实现线程间互斥:

#include <iostream>
#include <windows.h>
#include <process.h>int g_count = 0;HANDLE g_ThreadEvent , g_threadMutex, g_threadSemaphore ;unsigned int __stdcall ThreadFun (void * pthread_num)
{//WaitForSingleObject(g_ThreadEvent,INFINITE);WaitForSingleObject( g_threadSemaphore ,INFINITE );int num = *(( int *)pthread_num );//std::cout<<"线程号:"<<GetCurrentThreadId()<<"线程编号为地址:"<<&num<<"线程编号"<<num<<"全局资源编号为:"<<++g_count<<std::endl;std:: cout<< "线程编号" << num<< "全局资源编号为:" <<++ g_count<< std ::endl ;//std::cout<<"进程号:"<<(int)getpid()<<"线程号:"<<GetCurrentThreadId()<<std::endl;//SetEvent(g_ThreadEvent);// ReleaseMutex(g_threadMutex);ReleaseSemaphore( g_threadSemaphore ,1,NULL );return 0;
}int main ()
{const int thread_num = 10;g_threadSemaphore = CreateSemaphore (NULL ,1,1, NULL);//g_ThreadEvent = CreateEvent(NULL,false,false,NULL);//g_threadMutex = CreateMutex(NULL,false,NULL);//SetEvent(g_ThreadEvent); //在此处触发事件HANDLE handle [thread_num ];for ( int i = 0 ; i <thread_num ; ++ i ){handle [i ] = ( HANDLE) _beginthreadex (NULL ,0, ThreadFun,& i ,0,NULL );//WaitForSingleObject(g_threadMutex,INFINITE);}//等待所有创建的子线程都执行完.WaitForMultipleObjects( thread_num ,handle , true, INFINITE);//CloseHandle(g_ThreadEvent);//CloseHandle(g_threadMutex);CloseHandle( g_threadSemaphore );return 0;
}



好了,我们来实现一个信号量进行同步的操作吧.

#include <iostream>
#include <windows.h>
#include <process.h>int g_count = 0;HANDLE g_ThreadEvent , g_threadMutex, g_threadSemaphore ;unsigned int __stdcall ThreadFun (void * pthread_num)
{//WaitForSingleObject(g_ThreadEvent,INFINITE);//WaitForSingleObject(g_threadSemaphore,INFINITE);int num = *(( int *)pthread_num );//std::cout<<"线程号:"<<GetCurrentThreadId()<<"线程编号为地址:"<<&num<<"线程编号"<<num<<"全局资源编号为:"<<++g_count<<std::endl;std:: cout<< "线程编号" << num<< "全局资源编号为:" <<++ g_count<< std ::endl ;//std::cout<<"进程号:"<<(int)getpid()<<"线程号:"<<GetCurrentThreadId()<<std::endl;//SetEvent(g_ThreadEvent);// ReleaseMutex(g_threadMutex);ReleaseSemaphore( g_threadSemaphore ,1,NULL );return 0;
}int main ()
{const int thread_num = 10;g_threadSemaphore = CreateSemaphore (NULL ,0,1, NULL);//g_ThreadEvent = CreateEvent(NULL,false,false,NULL);//g_threadMutex = CreateMutex(NULL,false,NULL);//SetEvent(g_ThreadEvent); //在此处触发事件HANDLE handle [thread_num ];for ( int i = 0 ; i <thread_num ; ++ i ){handle [i ] = ( HANDLE) _beginthreadex (NULL ,0, ThreadFun,& i ,0,NULL );//WaitForSingleObject(g_threadMutex,INFINITE);WaitForSingleObject (g_threadSemaphore , INFINITE);}//等待所有创建的子线程都执行完.WaitForMultipleObjects( thread_num ,handle , true, INFINITE);//CloseHandle(g_ThreadEvent);//CloseHandle(g_threadMutex);CloseHandle( g_threadSemaphore );return 0;
}

这篇关于多线程第六篇:信号量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1102971

相关文章

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro

Java 多线程概述

多线程技术概述   1.线程与进程 进程:内存中运行的应用程序,每个进程都拥有一个独立的内存空间。线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换、并发执行,一个进程最少有一个线程,线程实际数是在进程基础之上的进一步划分,一个进程启动之后,进程之中的若干执行路径又可以划分成若干个线程 2.线程的调度 分时调度:所有线程轮流使用CPU的使用权,平均分配时间抢占式调度

Java 多线程的基本方式

Java 多线程的基本方式 基础实现两种方式: 通过实现Callable 接口方式(可得到返回值):

JAVA- 多线程

一,多线程的概念 1.并行与并发 并行:多个任务在同一时刻在cpu 上同时执行并发:多个任务在同一时刻在cpu 上交替执行 2.进程与线程 进程:就是操作系统中正在运行的一个应用程序。所以进程也就是“正在进行的程序”。(Windows系统中,我们可以在任务管理器中看 到进程) 线程:是程序运行的基本执行单元。当操作系统执行一个程序时, 会在系统中建立一个进程,该进程必须至少建立一个线

多线程篇(阻塞队列- LinkedBlockingDeque)(持续更新迭代)

目录 一、LinkedBlockingDeque是什么 二、核心属性详解 三、核心方法详解 addFirst(E e) offerFirst(E e) putFirst(E e) removeFirst() pollFirst() takeFirst() 其他 四、总结 一、LinkedBlockingDeque是什么 首先queue是一种数据结构,一个集合中

多线程篇(阻塞队列- LinkedBlockingQueue)(持续更新迭代)

目录 一、基本概要 1. 构造函数 2. 内部成员 二、非阻塞式添加元素:add、offer方法原理 offer的实现 enqueue入队操作 signalNotEmpty唤醒 删除线程(如消费者线程) 为什么要判断if (c == 0)时才去唤醒消费线程呢? 三、阻塞式添加元素:put 方法原理 图解:put线程的阻塞过程 四、非阻塞式移除:poll方法原理 dequ

spring笔记 多线程的支持

spring的工作机制 136  属性编辑器 140 spring事件的体系结构 168 Bean间的关系 109 继承 依赖 引用     Bean的继承          1 为了简化初始化的属性注入;          2 子Bean和父Bean相同的属性值,使用子Bean的     Bean的依赖 Srping控制相互依赖的Bean之间,属性注入的顺序,防止出错  depend-on

【编程底层思考】详解Java的JUC多线程并发编程底层组件AQS的作用及原理

Java中的AbstractQueuedSynchronizer(简称AQS)是位于java.util.concurrent.locks包中的一个核心组件,用于构建锁和其他同步器。AQS为实现依赖于FIFO(先进先出)等待队列的阻塞锁和相关同步器提供了一套高效、可扩展的框架。 一、AQS的作用 统一同步状态管理:AQS提供了一个int类型的成员变量state,用于表示同步状态。子类可以根据自己

多线程的系列文章

Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1)   Java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Java多线程学习(四)等待/通知(wait/notify)机制 Java多线程学习(五)线程间通信知识点补充 Java多线程学习(六)Lock锁的使用 Java多

多线程 线程池的创建

一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的。在jdk1.5之后这一情况有了很大的改观。Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用。为我们在开发中处理线程的问题提供了非常大的帮助。 二:线程池 线程池的作用: 线程池作用就是限制系统中执行线程的数量。