进程间通信之-----信号量

进程间通信简单的说有三个问题,第一个问题是:一个进程如何把信息传递给另一个,第二个问题是:要确保两个或者更多的进程在互动中不会出现交叉(即是进程互斥问题),第三个问题是:进程间同步问题、

四种进程或者线程同步互斥的控制方法

1):临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。

2):互斥量:为协调共同对一个共享资源的单独访问而设计的

3):信号量:为控制一个具有有限数量用户资源而设计

4):事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。

临界区(Critical Section)

  保证在某一时刻只有一个线程能访问数据的简便办法,在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开,临界区在被释放后,其他线程可以继续抢占,并以此达到原子方式操作共享资源的目的。

  临界区包含两个操作原语:

EnterCriticalSection() 进入临界区 
LeaveCriticalSection() 离开临界区 
虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。

互斥量(Mutex)

  互斥量跟临界区很相似,只有拥有互斥量对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其它线程在获得以访问资源,互斥量比临界区复杂。使用互斥量不仅仅可以在同一进程的不同线程中实现资源的共享,而且还可以在不同进程的线程之间实现对资源的安全访问。

互斥量包含的几个操作原语:

CreateMutex() 创建一个互斥量 
OpenMutex() 打开一个互斥量 
ReleaseMutex() 释放互斥量 
WaitForMultipleObjects() 等待互斥量对象

信号量(Semaphores)

  信号量对象对线程的同步方式与上面的两种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的pv操作相同,它指出了同时访问共享资源的线程最大数目,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用CreateSemaphone()创建信号量时既要同时指出允许的最大资源计数和当前可用资源计数,一般是将挡墙可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减去1,只要当前可用资源计数是大于0,就可以发出信号量信号,但是当前可用计数减小到0则说明当前占用资源的线程数已经达到了所允许的最大数目,不能再允许其他线程的进入,此时的信号量将无法发出,线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphone()函数将当前可用资源计数加1,在任何时候当前可用资源计数决不能大于最大资源计数。

信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但s小于零时则表示正在等待使用共享资源的进程数。

p操作申请资源:

  1)s减去1

  2)若s减1仍大于0,则进程继续执行。

  3)若s减1小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转入进程调度。

V操作释放资源

  1)s加1

  2)若相加结果大于零,则进程继续执行。

  3)若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转入进程调度、

信号量包含的几个操作原语: 
     CreateSemaphore() 创建一个信号量 
     OpenSemaphore() 打开一个信号量 
     ReleaseSemaphore() 释放信号量 
     WaitForSingleObject() 等待信号量

事件(Event)

事件对象也可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。 
信号量包含的几个操作原语: 
     CreateEvent() 创建一个事件 
     OpenEvent() 打开一个事件 
     SetEvent() 回置事件 
     WaitForSingleObject() 等待一个事件 
     WaitForMultipleObjects()         等待多个事件 
     WaitForMultipleObjects 函数原型: 
     WaitForMultipleObjects( 
       IN DWORD nCount, // 等待句柄数 
       IN CONST HANDLE *lpHandles, //指向句柄数组 
       IN BOOL bWaitAll, //是否完全等待标志 
       IN DWORD dwMilliseconds //等待时间 
       ) 
参 数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。 dwMilliseconds在这里的作用与在WaitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回 WAIT_TIMEOUT

信号量是一种解决进程同步的解决方案,同时信号量也可以当做互斥量来使用,使用信号量来解决进程同步问题是编程中使用较多的方法。

使用信号量比较经典的问题时生产者消费者问题:下面举个例子说明

由于本例是在windows下写的代码,在windows下使用多线程编程,需要包含pthread.h.在ftp://sourceware.org/pub/pthreads-win32网址下、

我下载的是pthreads-w32-2-7-0-release.exe.双击pthreads-w32-2-7-0-release.exe,点击Browse选择安装到的目录,然后点击Extract解压,完成后点击Done。

之后会在安装目录看到有三个文件夹Pre-built.2、pthreads.2、QueueUserAPCEx.

将Pre-built.2文件夹下的include和lib文件夹里的文件复制到VS对应的include和lib目录,我这里是C:\Program Files\Microsoft Visual Studio 11.0\VC\include和C:\Program Files\Microsoft VisualStudio 11.0\VC\lib.这样就可以了,如果运行的时候链接错误,把pthreadVC2.dll 拷贝到你程序的可执行目录下试试。

  1 #include "stdafx.h"
  2 #include <stdio.h>
  3 #include <pthread.h>
  4 #include <sched.h>
  5 #include <semaphore.h>
  6 #include <conio.h>
  7 #include <ctype.h>
  8 #include <signal.h>
  9 #include <iostream>
 10
 11 #include<Windows.h>
 12 using namespace std;
 13 #pragma comment(lib,"pthreadVC2.lib")
 14
 15 #define N 5  //消费者或者生产者的数目
 16 #define M 10  //缓冲数目
 17
 18 int productin = 0; //生产者放置产品的位置
 19 int prochaseout = 0; //消费者取产品的位置
 20
 21 int buff[M] = {0}; //缓冲区初始化为0,开始时没有产品。
 22
 23 sem_t empty_sem; // 同步信号量,当满的时候阻止生产者放产品。
 24 sem_t full_sem; //同步信号量,当没有产品的时候阻止消费者消费。
 25
 26 pthread_mutex_t mutex; //互斥信号量,一次只有一个线程访问缓冲区。
 27
 28 int product_id = 0; //生产者id
 29 int prochase_id = 0; //消费者id
 30
 31 void SignalExit(int signo)
 32 {
 33     printf("程序退出%d\n",signo);
 34     return;
 35 }
 36
 37 void PrintProduction()
 38 {
 39     printf("此时的产品队列为::");
 40     for(int i = 0; i < M; i++ )
 41     {
 42         printf("%d  ",buff[i]);
 43     }
 44     printf("\n\n");
 45 }
 46
 47 //////////////////////生产者方法////////////////////
 48 void* Product(void* pramter)
 49 {
 50     int id = ++product_id;
 51     while(1)
 52     {
 53         Sleep(5000); //毫秒
 54         sem_wait(&empty_sem); //给信号量减1操作
 55         pthread_mutex_lock(&mutex);
 56         productin = productin % M;
 57         printf("生产者%d在产品队列中放入第%d个产品\n\n",id,productin+1);
 58         buff[productin] = 1;
 59         PrintProduction();
 60         ++productin;
 61
 62         pthread_mutex_unlock(&mutex); //释放互斥量对象
 63         sem_post(&full_sem); //给信号量的值加1操作
 64     }
 65 }
 66
 67 //////////////消费者方法///////////////////////
 68 void* Prochase( void* pramter )
 69 {
 70     int id = ++prochase_id;
 71     while(1)
 72     {
 73         Sleep(7000);
 74         sem_wait(&full_sem);
 75         pthread_mutex_lock(&mutex);
 76         prochaseout = prochaseout % M;
 77         printf("消费者%d从产品队列中取出第%d个产品\n\n",id,prochaseout+1);
 78
 79         buff[prochaseout] = 0;
 80         PrintProduction();
 81         ++prochaseout;
 82
 83         pthread_mutex_unlock(&mutex);
 84         sem_post(&empty_sem);
 85     }
 86 }
 87
 88 int main()
 89 {
 90     cout << "生产者和消费者数目都为5,产品缓冲区为10,生产者每2秒生产一个产品,消费者每5秒消费一个产品" << endl << endl;
 91     pthread_t productid[N];
 92     pthread_t prochaseid[N];
 93
 94     int ret[N];
 95
 96     //初始化信号量
 97     int seminit1 = sem_init(&empty_sem,0,M);
 98     int seminit2 = sem_init(&full_sem,0,0);
 99     if( seminit1 != 0  && seminit2 != 0 )
100     {
101         printf("sem_init failed !!!\n");
102         return 0;
103     }
104
105     //初始化互斥信号量
106     int mutexinit = pthread_mutex_init(&mutex,NULL);
107     if( mutexinit != 0 )
108     {
109         printf("pthread_mutex_init failed !!\n");
110         return 0;
111     }
112
113     //创建n个生产者线程
114     for(int i = 0; i < N; i++ )
115     {
116         ret[i] = pthread_create( &productid[i], NULL,Product,(void*)(&i) );
117         if( ret[i] != 0 )
118         {
119             printf("生产者%d线程创建失败!\n",i);
120             return 0;
121         }
122     }
123
124     //创建n个消费者线程
125     for(int j = 0; j < N; j++ )
126     {
127         ret[j] = pthread_create(&prochaseid[j],NULL,Prochase,NULL);
128         if( ret[j] != 0 )
129         {
130             printf("消费者%d线程创建失败\n",j);
131             return 0;
132         }
133     }
134
135     ///////////////////////等待线程被销毁///////////////////////////////////////////////
136     for( int k = 0; k < N; k++ )
137     {
138         printf("销毁线程\n");
139         pthread_join(productid[k],NULL);
140         pthread_join(prochaseid[k],NULL);
141     }
142     return 0;
143 }
时间: 2024-12-19 13:37:01

进程间通信之-----信号量的相关文章

VxWorks进程间通信2 -- 信号量

VxWorks进程间通信2 -- 信号量一.信号量的概念 是实现任务互斥.同步操作的主要机制.VxWorks提供的信号量经过了高度优化,在所有任务间通信机制中,速度最快. 二.信号量的分类 Binary Semaphores(二进制):完成互斥.同步操作的最佳方式:速度最快,最常用. Mutual Exclusion Semaphores(互斥):一种特殊的二进制信号量,专门针对互斥操作进行了优化. Counting Semaphores(计数):类似于二进制信号量,可记录信号量释放的次数,可监

Linux进程间通信:信号量

上次书写了进程间通信的消息队列,这次是IPC中的另一个模块.信号量 信号量是什么? 荷兰计算机科学家Dijkstra把互斥的关键含义抽象称为信号量(semaphore)概念.信号量是一个被保护的量. 信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的 通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识.信号 量在此过程中负责数据操作的互斥.同步等功能. 当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可 用.大于0,资源可

在Linxu下进程间通信之信号量

(一) 信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识.信号量在此过程中负责数据操作的互斥.同步等功能. 当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用.当信号量的值大于0时,资源可以请求,等于0,无资源可用,进程会进入睡眠状态直到资源可用.当进程不再使用一个信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减操作均为原子操作,这是由于信号量主要的作用是

进程间通信之信号量

一.对信号量的理解 信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识. 当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用.大于0,资源可以请求,小于0,无资源可用,进程会进入睡眠状态直至资源可用.当进程不再使用一个信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减操作均为原子操作. 二.使用信号量的原因 防止出现因多个程序同时访问一个共享资源时而引发的一系

Linux进程间通信——使用信号量(转)

这篇文章将讲述别一种进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信--使用信号.下面就进入信号量的讲解. 一.什么是信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域.临界区域是指执行数据更新的代码需要独占式地执行.而信号量就可以提供这样的一种访问机制,让一个临界区同

Linux进程间通信——使用信号量【转】

本文转载自:http://blog.csdn.net/ljianhui/article/details/10243617 这篇文章将讲述别一种进程间通信的机制——信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信——使用信号.下面就进入信号量的讲解. 一.什么是信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线

Linux进程间通信--使用信号量【转】

本文转载自:http://blog.csdn.net/ljianhui/article/details/10243617 这篇文章将讲述别一种进程间通信的机制——信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信——使用信号.下面就进入信号量的讲解. 一.什么是信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线

Linux进程间通信——使用信号量

这篇文章将讲述别一种进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信--使用信号.下面就进入信号量的讲解. 一.什么是信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域.临界区域是指执行数据更新的代码需要独占式地执行.而信号量就可以提供这样的一种访问机制,让一个临界区同

Linux进程间通信:管道,信号量,消息队列,信号,共享内存,套接字

Linux下的进程通信手段基本上是从UNIX平台上的进程通信手段继承而来的.而对UNIX发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间的通信方面的侧重点有所不同.前者是对UNIX早期的进程间通信手段进行了系统的改进和扩充,形成了“system V IPC”,其通信进程主要局限在单个计算机内:后者则跳过了该限制,形成了基于套接口(socket)的进程间通信机制.而Linux则把两者的优势都继承了下来 linux进程间通信(IPC)有几种方式