线程同步与互斥(POSIX信号量——环形数组实现生产者消费者模型)

Semaphore(信号量)

Mutex变量是非0即1的,可看作一种资源的可用数量,初始化时Mutex是1,表示有一个可用资源,加锁时获得该资源,将Mutex减到0,表示不再有可用资源,解锁时释放该资源,将Mutex重新加到1,表示又有了一个可用资源。

信号量(Semaphore)和Mutex类似,表示可用资源的数量,和Mutex不同的是这个数量可以大于1。如果信号量描述的资源数目是1时,此时的信号量和互斥锁相同!

POSIX semaphore库函数,这种信号量不仅可用于同一进程的线程间同步,也可用于不同进程间的同步。

有关函数:

单生产者但消费者模型:

代码说明:

1.push()和pop()的数据(datatype)本篇用的是基本类型,如果是自定义类型的话,需要实现赋值运算符的重载

2.数组实际上是线性的,内存中并没有环形数组,我们定义了一个固定大小的数组,当数组的最后一个元素也被填上数据时,检查数组的第一个元素(下标为0的元素)是否已经被消费者读过,如果已经读过,那么生产者就可以继续放数据,当数组满时(即数组中的元素一个也没有被消费者读),生产者会等待。

3.sem_init()初始化两个信号量sem_p(控制生产者)和sem_c(控制消费者),pshared为0时,表示信号量用于同一进程的线程间同步

 sem_destroy()使两个信号量回到初始化前的状态

 sem_wait() 可以获得资源,相当于P操作,把给定信号量减一

 sem_post() 可以释放资源,相当于V操作,进行加一操作

 当信号量的值为0时,sem_wait()会将进程挂起等待,sem_trywait()不会将进程挂起


代码实现:

ring.cpp

#include <iostream>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
using namespace std;

#define SEM_PRO 20
#define SEM_CON 0
#define SIZE SEM_PRO

typedef int datatype; 

datatype ring[SIZE];//数组的定义
datatype pro,con;

sem_t sem_p;//product
sem_t sem_c;//consumer

void init_ring(datatype (*ring)[SIZE])
{
    pro=0;
    con=0;
 }

datatype& push(datatype &data,int index)
{
    ring[pro++]=data;
    datatype tmp=ring[pro-1];
    pro%=SIZE;
    return tmp;
}

datatype& pop(int index)
{
    con++;
    datatype tmp=ring[con-1];
    con%=SIZE;
    return tmp;
}

void* product(void* arg)
{
    while(1){
        datatype data=rand()%50;
        sem_wait(&sem_p);
        datatype val=push(data,pro);
        cout<<"product done...,val is:"<<val<<endl;
        sem_post(&sem_c);
        sleep(1);
    }
}

void* consumer(void* arg)
{
    while(1){
        sem_wait(&sem_c);
        datatype val=pop(con);
        cout<<"consumer done...,val is:"<<val<<endl;
        sem_post(&sem_p);
        sleep(8);
    }
}

int main()
{
    init_ring(&ring);

    sem_init(&sem_p,0,SEM_PRO);
    sem_init(&sem_c,0,0);

    pthread_t tid1,tid2;
    pthread_create(&tid1,NULL,product,NULL);
    pthread_create(&tid2,NULL,consumer,NULL);

    sem_destroy(&sem_p);
    sem_destroy(&sem_c);

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    return 0;
}

Makefile

ring:ring.cpp
    g++ -o [email protected] $^ -lpthread

.PHONY:clean
clean:
    rm -f ring

下面两次运行生产者和消费者的速度有所变化,导致运行结果不同

第一次运行结果:

第二次运行结果:

多生产者多消费者模型:(实际上和单生产者单消费者一样,只多加了6行线程创建和等待的代码)

#include <iostream>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
using namespace std;

#define SEM_PRO 20
#define SEM_CON 0
#define SIZE SEM_PRO

typedef int datatype;

datatype ring[SIZE];
datatype pro,con;

sem_t sem_p;//product
sem_t sem_c;//consumer

void init_ring(datatype (*ring)[SIZE])
{
    pro=0;
    con=0;
}

datatype& push(datatype &data,int index)
{
    ring[pro++]=data;
    datatype tmp=ring[pro-1];
    pro%=SIZE;
    return tmp;
}

datatype& pop(int index)
{
    con++;
    datatype tmp=ring[con-1];
    con%=SIZE;
    return tmp;
}

void* product(void* arg)
{
    while(1){
        datatype data=rand()%50;
        sem_wait(&sem_p);
        datatype val=push(data,pro);
        sem_wait(&sem_p);
        datatype val=push(data,pro);
        cout<<"product"<<(int)arg<<" done...,val is:"<<val<<endl;
        sem_post(&sem_c);
        sleep(1);
    }
}

void* consumer(void* arg)
{
    while(1){
        sem_wait(&sem_c);
        datatype val=pop(con);
        cout<<"consumer"<<(int)arg<<" done...,val is:"<<val<<endl;
        sem_post(&sem_p);
        sleep(3);
    }
}

int main()
{
    init_ring(&ring);

    sem_init(&sem_p,0,SEM_PRO);
    sem_init(&sem_c,0,0);

    pthread_t tid1,tid2,tid3;
    pthread_create(&tid1,NULL,product,(void*)1);
    pthread_create(&tid2,NULL,product,(void*)2);
    pthread_create(&tid3,NULL,product,(void*)3);

    pthread_t tid4,tid5;
    pthread_create(&tid4,NULL,consumer,(void*)4);
    pthread_create(&tid5,NULL,consumer,(void*)5);

    sem_destroy(&sem_p);
    sem_destroy(&sem_c);

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    pthread_join(tid5,NULL);

    return 0;
}

运行结果:

时间: 2024-10-04 10:05:05

线程同步与互斥(POSIX信号量——环形数组实现生产者消费者模型)的相关文章

13 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件  queue队列 生产者消费者模型 Queue队列 开发一个线程池

本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Queue队列 开发一个线程池 进程 语法 进程间通讯 进程池 操作系统发展史 手工操作(无操作系统) 1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统,计算机工作采用手工操作方式. 手工操作程序员将对应于程序和数据的已穿孔的纸带(或卡片)装入输入机,然后启动输入机把

Python学习笔记——进阶篇【第九周】———线程、进程、协程篇(队列Queue和生产者消费者模型)

Python之路,进程.线程.协程篇 本节内容 进程.与线程区别 cpu运行原理 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Queue队列 开发一个线程池 进程 语法 进程间通讯 进程池 参考链接http://www.cnblogs.com/alex3714/articles/5230609.html

线程同步与互斥:POSIX无名信号量

信号量概述 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问. 编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞.PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1. 信号量主要用于进程或线程间的同步和互斥这两种典型情况. 信号量用于互斥: 信号量用于同步: 在 POSIX 标准中,信号量分两种,一种是无名信号量,一种是有名信号量.无名信号量一般用

Linux系统编程——线程同步与互斥:无名信号量

信号量概述 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问. 编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞.PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1. 信号量主要用于进程或线程间的同步和互斥这两种典型情况. 信号量用于互斥: 信号量用于同步: 在 POSIX 标准中,信号量分两种,一种是无名信号量,一种是有名信号量.无名信号量一般用

LinuxC线程pthread线程同步进程同步-互斥量、信号量、条件变量、读写锁、文件锁

1. 同步概念 2. 线程同步 3. 进程同步 4. 生产者消费者模型 5. 哲学家问题 原文地址:https://www.cnblogs.com/yongfengnice/p/12116954.html

Linux环境编程之同步(四):Posix信号量

信号量是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语.有三种类型:Posix有名信号量,使用Posix IPC名字标识:Posix基于内存的信号量,存放在共享内存区中:System V信号量,在内核中维护.这三种信号量都可用于进程间或线程间的同步. 图1 由两个进程使用的一个二值信号量 图2 由两个进程使用的一个Posix有名二值信号量 图3 由一个进程内的两个线程共享的基于内存的信号量 一个进程可以在某个信号量上执行的三种操作: 1.创建一个信号量,这要求调用者指定初始值,对

线程同步与互斥

临界资源: 一个进程的资源对于运行在它内部的线程是共享的,一次只允许一个线程使用的资源叫做临界资源 临界区: 访问临界资源的那段程序叫做临界区 线程的同步: 同步就是协同步调,按照预定的先后顺序执行. "同"字应是指协同.协助.互相配合. 线程的互斥: 某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性. 但互斥无法限制访问者对资源的访问顺序,即访问是无序的. 同步的方法(互斥量.条件变量.信号量) 互斥量(mutex) 多个线程同时访问共享数据时,经常会发生冲突,因此常常引

一起talk C栗子吧(第一百一十六回:C语言实例--线程同步之互斥量二)

各位看官们,大家好,上一回中咱们说的是线程同步之信号量的例子,这一回咱们继续说该例子.闲话休提,言归正转.让我们一起talk C栗子吧! 我们在上一回中详细介绍了互斥量相关函数的用法,这一回中,我们介绍如何使用这些函数来操作互斥量. 下面是详细的操作步骤: 1.定义一个互斥量A,用来同步线程: 2.在创建线程的进程中使用pthread_mutex_init函数初始化互斥量,互斥量的属性使用默认值: 3.在读取数据的线程中读取数据,首先使用pthread_mutex_lock函数对互斥量A进行加锁

Android多线程研究(3)——线程同步和互斥及死锁

为什么会有线程同步的概念呢?为什么要同步?什么是线程同步?先看一段代码: package com.maso.test; public class ThreadTest2 implements Runnable{ private TestObj testObj = new TestObj(); public static void main(String[] args) { ThreadTest2 tt = new ThreadTest2(); Thread t1 = new Thread(tt,