[Linux] Semáforo POSIX de subprocesos múltiples

1. Concepto

Un semáforo, también conocido como semáforo
, es esencialmente un contador que se utiliza para describir la cantidad de recursos críticos.

sem: 0 -> 1 -> 0
Si solo hay 1 recurso crítico, establezca sem en 1. Cuando se van a usar recursos críticos, sem cambia de 1 a 0. Otros quieren solicitar, pero no pueden solicitar la cola pendiente. Cuando se espera que se liberen recursos críticos, sem cambia de 0 a 1 antes de volver a solicitar recursos críticos. Este semáforo se denomina semáforo binario, que es equivalente a un bloqueo
mutex

Cada subproceso, al acceder al recurso correspondiente, primero solicita el semáforo. Si la aplicación tiene
éxito, significa que el subproceso puede usar el recurso . Si la
aplicación no tiene éxito, significa que el recurso no se puede usar en este momento.

2. El mecanismo de trabajo del semáforo.

El mecanismo del semáforo es similar a ver una película y comprar entradas, es un mecanismo de reserva de recursos
, si el semáforo se aplica con éxito, equivale a reservar una parte de los recursos.

Juzgar si se cumplen las condiciones determina el comportamiento de seguimiento.
El semáforo ya es un contador de recursos. La aplicación exitosa del semáforo indica que el recurso está disponible. La
falla de la aplicación del semáforo indica que el recurso no está disponible.
La esencia es convertir el juicio en una aplicación de semáforo.

3. Conoce la interfaz

El semáforo POSIX y el semáforo del sistema V tienen la misma función, ambos se usan para la operación síncrona para lograr el propósito de acceder a los recursos compartidos sin conflicto, pero POSIX se puede usar para la sincronización entre subprocesos


sem_init - inicializa el semáforo

Introduzca hombre sem_init

sem: indica el semáforo
pshared: 0 indica compartido entre subprocesos, distinto de cero indica compartido entre procesos
value: el valor inicial del semáforo (cuánto se inicializa el valor del contador)

sem_destroy - destruir semáforo

Introduzca el hombre sem_destroy

Destruir el semáforo inicializado

sem_wait - solicitar semáforo

Ingrese hombre sem_wait

Realizar la operación de solicitar el semáforo para reducir el valor del semáforo en 1

sem_post - liberar semáforo

Introduzca hombre sem_post

Realizar la operación de soltar el semáforo para aumentar el valor del semáforo en 1

4. Modelo de producción y consumo basado en ring queue

Análisis de principios

La cola de espera en realidad se simula usando una matriz

Un espacio adicional en la matriz es para resolver el problema de juzgar completo


Si está vacío, el hilo y la cola están en la misma posición


Si está lleno, la siguiente posición de cola es cabeza.


El productor empuja los datos a la cola , es decir, el
consumidor de producción saca los datos a la cabeza, es decir, el consumo.


¿Están los productores y los consumidores preocupados por los mismos recursos?
No es lo mismo, el productor se preocupa por el espacio de toda la cola del anillo (si la tienda está llena de bienes)
el consumidor se preocupa por los datos (si hay bienes en la tienda, cómprelos si hay bienes)


¿Cuándo acceden la cabeza y la cola a la misma región?
Hay una mesa grande con un área de escala de 0 a 12 en punto como un reloj, y se coloca un plato en cada escala.
Dos personas A y B entran a la habitación al mismo tiempo y ven la mesa. A pone una manzana en el plato y B toma la manzana detrás.
A y B están de acuerdo: B no puede exceder a A, y solo se puede colocar una manzana en un plato.

Cuando A y B comienzan, cuando no hay manzanas en la mesa, o cuando la mesa está llena de manzanas, visitarán el mismo plato, es decir, la cola del anillo está vacía, o la cola del anillo está llena, y visitarán la misma
área .

Cuando la cola está vacía y apunta a la misma ubicación, existe una relación competitiva,
deje que el productor ejecute primero (solo después de que el productor produzca datos, el consumidor puede obtener los datos)

Cuando la cola esté llena, apunte a la misma ubicación, hay una relación competitiva,
deje que el consumidor corra primero (el productor solo puede producir después de que el consumidor obtenga los datos)


Los productores se preocupan por el espacio, y el espacio en sí también es un recurso, por lo que se debe definir un semáforo sem_room para el productor, y su valor inicial es N
P(sem_room) —— Solicite un semáforo espacial

Si los datos de producción del productor están en el espacio actual, los datos correspondientes serán +1, por lo que el consumidor puede obtener los datos
V (sem_data) - el valor del semáforo de datos +1

Los consumidores se preocupan por los datos, el semáforo es sem_data y su valor inicial es 0
P(sem_data) —— Solicitar semáforo de datos

El consumidor retira los datos y el espacio actual está inactivo, por lo que el productor puede colocar los datos
V(sem_room) - el valor del semáforo espacial +1

el código

análisis de código

Primero cree una clase ringqueue en ringqueue.hpp


Use new en la función principal para crear una cola rq
Para garantizar que los productores y los consumidores vean el mismo recurso, el parámetro args de ambas funciones de devolución de llamada es rq


La función de devolución de llamada del productorRoutine usa el empuje de la cola rq para insertar datos en la cola, es decir , producción


clase de cola de llamada

En la clase ringqueue

Al explicar el principio anterior, solo el consumidor se preocupa por el semáforo de datos, y solo el productor se preocupa por el semáforo espacial.

estructura

Inicialice el tamaño del anillo y _cap (capacidad) de la cola del anillo a N

0 significa compartir entre subprocesos, inicialice el semáforo de datos en 0 e inicialice el semáforo de espacio a la capacidad de toda la cola del anillo
(para el valor de inicialización de ambos, hay explicaciones detalladas en el principio)

destruir

Dado que el semáforo se inicializa en el momento de la construcción, el semáforo debe destruirse

empujar - producción

Antes de la producción, es necesario asegurarse de que se cumplan las condiciones antes de que se pueda llevar a cabo la producción, por lo que se requiere la operación P: solicitar semáforo

Cuando se usa un semáforo, no hay necesidad de juzgar
porque el semáforo es un contador. La esencia es transferir la situación lista de los recursos desde dentro del área crítica hacia fuera del área crítica. En sí mismo
describe la cantidad de recursos críticos, por lo que no hay necesidad de juzgar si los recursos críticos cumplen con las condiciones después de ingresar al área crítica.


Los productores y los consumidores pueden visitar el mismo lugar, pero es muy probable que visiten lugares diferentes,
por lo que los productores y los consumidores deben tener sus propios subíndices para indicar sus lugares.


Realice continuamente operaciones P para insertar datos en el espacio.
Si no hay espacio, los consumidores deben consumir (operaciones V) y retirar los datos.

Cuando la cola alcanza una posición de espacio adicional, en realidad es equivalente a volver al comienzo de la cabeza nuevamente,
así que use %= para simular una cola de anillo


Encapsule sem_wait y sem_post con la ayuda de las funciones P y V.
Cuando se use nuevamente, solo necesita llamar a PV para lograr

inserte la descripción de la imagen aquí

pop - consumo

Realice continuamente operaciones P para eliminar datos del espacio, y cuando el espacio esté inactivo,
los productores deben producir (operaciones V) y colocar datos en el espacio.

Código

Ringqueue.hpp


#include<iostream>
#include<vector>
#include<semaphore.h>//信号量头文件
static const int N=5;//设置环形队列的大小

template<class T>
class ringqueue
{
    
    
private:
  void P(sem_t &s)
  {
    
    
     sem_wait(&s); 
  }
  void V(sem_t&s)
  {
    
    
     sem_post(&s);
  }
public:
    ringqueue(int num=N)
    : _ring(num),_cap(num)
    {
    
    
        //信号量初始化
        sem_init(&_data_sem,0,0);
        sem_init(&_space_sem,0,num);
        _c_step=_p_step=0;//生产和消费下标都为0
    }
    ~ringqueue()
    {
    
    
        //销毁信号量
     sem_destroy(&_data_sem);
     sem_destroy(&_space_sem);
    }

    void push(const T&in)//生产
    {
    
    
      P(_space_sem);//P操作 申请信号量
      _ring[_p_step++]=in;//将数据放入生产位置
      _p_step%=_cap;
      V(_data_sem);//V操作 释放信号量
    }
    void pop(T*out)//消费 
    {
    
    
       P(_data_sem);//P操作
       *out=_ring[_c_step++];//将该位置的数据给与out
       _c_step%=_cap;
       V(_space_sem);//V操作
    }
private:
   int _c_step;//消费者位置下标
   int _p_step;//生产者位置下标
   std::vector<int> _ring;//充当环形队列
   int  _cap;//环形队列的容器大小
   sem_t _data_sem;//数据信号量
   sem_t _space_sem;//空间信号量 
};

archivo MAKE

ringqueue:main.cc
	g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -f ringqueue 

principal.cc


#include"Ringqueue.hpp"
#include<pthread.h>
#include<unistd.h>
using namespace std;
void*consumerRoutine(void*args)
{
    
    
  ringqueue<int>*rq=(ringqueue<int>*)args;
  while(true)
  {
    
    
  int data=0;
  rq->pop(&data);//从队列取出数据 消费
  cout<<"consumer done:"<<data<<endl;
  sleep(1);
  }
}
void*productorRoutine(void*args) 
{
    
    
 ringqueue<int>*rq=(ringqueue<int>*)args;
 while(true)
 {
    
    
   int data=1;
   rq->push(data);//将数据插入队列中 生产
   cout<<"productor done:"<<data<<endl;
 }
}

int main()
{
    
    
   ringqueue<int>*rq=new ringqueue<int>();
   pthread_t c;//消费者
   pthread_t p;//生产者
   //创建线程
   pthread_create(&c,nullptr,consumerRoutine,rq);
     pthread_create(&p,nullptr,productorRoutine,rq);

     pthread_join(c,nullptr);
     pthread_join(p,nullptr);
    return 0;
}

Supongo que te gusta

Origin blog.csdn.net/qq_62939852/article/details/131754311
Recomendado
Clasificación