Notas de estudio sobre sincronización de subprocesos, punto muerto mutex, modelos de productor y consumidor, y conocimientos relacionados con semáforos

1. Sincronización de subprocesos

La sincronización es el ritmo coordinado, corriendo en un orden predeterminado. Tales como: Terminas, diré. Literalmente, la palabra "igual" es fácil de entender como actuar juntos, pero no lo es. La palabra "igual" debe referirse a coordinación, asistencia y cooperación mutua.

Por ejemplo, la sincronización de procesos y subprocesos puede entenderse como la cooperación entre el proceso o subproceso A y B. Cuando A se ejecuta hasta cierto punto, depende de un determinado resultado de B, por lo que se detiene y le indica a B que se ejecute; dice, y luego le da el resultado a A, A continúa operando.

En la programación de subprocesos múltiples, varios subprocesos no pueden acceder a algunos datos confidenciales al mismo tiempo. En este momento, la tecnología de acceso síncrono se utiliza para garantizar que, como máximo, un subproceso acceda a los datos en cualquier momento para garantizar la integridad de los datos.

#include <stdio.h>
#include <pthread.h>

int num = 0;
void* run(void* arg)
{
    
    
	for(int i=0; i<1000000; i++)
	{
    
    
		num++;
	}
}

int main(int argc,const char* argv[])
{
    
    
	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,run,NULL);
	pthread_create(&tid2,NULL,run,NULL);
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	printf("%d\n",num);
}

2. Bloqueo de exclusión mutua

注意:如果man手册中查不到这系列函数,可以安装以下内容:
	sudo apt-get install glibc-doc
	sudo apt-get install manpages-posix-dev
    
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
功能:定义并初始化互斥锁
    
int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t*
mutexattr);
功能:初始化一互斥锁,会被初始化为非锁定状态
    
int pthread_mutex_lock (pthread_mutex_t* mutex);
功能:加锁,当互斥锁已经是锁定状态时,调用者会阻塞,直到互斥被解开,当前线程才会加锁成功并返回。
    
int pthread_mutex_unlock (pthread_mutex_t* mutex);
功能:解锁,解锁后等待加锁的线程才能加锁成功。
    
int pthread_mutex_destroy (pthread_mutex_t* mutex);
功能:销毁锁
    
int pthread_mutex_trylock (pthread_mutex_t *__mutex)
功能:加测试锁,如果不加锁刚立即返回
    
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
							const struct timespec *restrict abs_timeout);
功能:倒计时加锁,如果超时还不加上则立即返回。
struct timespec{
    
    
	time_t tv_sec; 		/* Seconds. */
	long int tv_nsec; 	/* Nanoseconds.*/ 1= 1000000000 纳秒
};
#include <stdio.h>
#include <pthread.h>
/*
执行流程:
	1、互斥锁被初始化为非锁定状态
	2、线程1调用pthread_mutex_lock函数,立即返回,互斥量呈锁定状态;
	3、线程2调用pthread_mutex_lock函数,阻塞等待;
	4、线程1调用pthread_mutex_unlock函数,互斥量呈非锁定状态;
	5、线程2被唤醒,从pthread_mutex_lock函数中返回,互斥量呈锁定状态
*/

pthread_mutex_t mutex;
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int num = 0;
void* run(void* arg)
{
    
    
	for(int i=0; i<1000000; i++)
	{
    
    
		pthread_mutex_lock(&mutex);
		num++;
		pthread_mutex_unlock(&mutex);
	}
}

int main(int argc,const char* argv[])
{
    
    
    pthread_mutex_init(&mutex,NULL);
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,run,NULL);
    pthread_create(&pid2,NULL,run,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    pthread_mutex_destroy(&mutex);
    printf("%d\n",num);
}

3. Bloqueo de lectura y escritura

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
功能:定义并初始化读写锁
    
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
						const pthread_rwlockattr_t *restrict attr);
功能:初始化读写锁
    
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
功能:加读锁,如果不能加则阻塞等待
    
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
功能:加写锁,如果不能加则阻塞等待
    
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
功能:尝试加读锁,如果不能加则立即返回
    
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
功能:尝试加写锁,如果不能加则立即返回
    
int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,
								const struct timespec *restrict abstime);
功能:带倒计时加读锁,超时则立即返回
    
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,
								const struct timespec *restrict abstime);
功能:带倒计时加写锁,超时则立即返回
    
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
功能:销毁读写锁
    
使用读写锁的线程应根据后续的操作进行加锁,如果只对数据进行读取则只加读锁即可,只有对数据进行修改时才应该加写锁,与互斥锁的区别是,它能让只读的线程加上锁,使用原理与文件锁一样。
    线程A 线程B
    读锁 读锁 OK
    读锁 写锁 NO
    写锁 读锁 NO
    写锁 写锁 NO
#include <pthread.h>

// 创建读写锁
pthread_rwlock_t rwlock;

int num = 0;
void* run(void* arg)
{
    
    
	for(int i=0; i<1000000; i++)
	{
    
    
		// 加写锁
		pthread_rwlock_wrlock(&rwlock);
		num++;
		// 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

int main(int argc,const char* argv[])
{
    
    
	// 初始化读写锁
    pthread_rwlock_init(&rwlock,NULL);
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,run,NULL);
    pthread_create(&pid2,NULL,run,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    printf("%d\n",num);
}

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

// 创建读写锁
pthread_rwlock_t rwlock;

void* run1(void* arg)
{
    
    
    // 加读锁
    pthread_rwlock_rdlock(&rwlock);
    sleep(1);
    printf("%lu 线程1加读锁成功!\n",pthread_self());
    // 解锁
    pthread_rwlock_unlock(&rwlock);
}
void* run2(void* arg)
{
    
    
    // 加读锁
    pthread_rwlock_rdlock(&rwlock);
    sleep(1);
    printf("%lu 线程2加读锁成功!\n",pthread_self());
    // 解锁
    pthread_rwlock_unlock(&rwlock);
}

int main(int argc,const char* argv[])
{
    
    
    // 初始化读写锁
    pthread_rwlock_init(&rwlock,NULL);
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,run1,NULL);
    pthread_create(&pid2,NULL,run2,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    // 销毁读写锁
    pthread_rwlock_destroy(&rwlock);
}

4. Problema de punto muerto

Qué es un punto muerto:

Múltiples subprocesos esperan los recursos de los demás y no liberarán sus propios recursos hasta que obtengan los recursos que necesitan, y luego causan un fenómeno de espera circular, que se denomina interbloqueo.

Hay cuatro condiciones necesarias para que se produzca un interbloqueo:

1. Exclusión mutua de recursos

2. Poseer y esperar

3. Los recursos son inalienables

4. Bucle de espera

Las cuatro condiciones anteriores son indispensables, mientras una de ellas no se cumpla, no puede constituir un callejón sin salida.

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

// 创建三个互斥锁并初始化
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;

void* run1(void* arg)
{
    
    
    pthread_mutex_lock(&mutex1);
    usleep(100);
    pthread_mutex_lock(&mutex2);
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
}

void* run2(void* arg)
{
    
    
    pthread_mutex_lock(&mutex2);
    usleep(100);
    pthread_mutex_lock(&mutex3);
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex3);
    pthread_mutex_unlock(&mutex2);
}

void* run3(void* arg)
{
    
    
    pthread_mutex_lock(&mutex3);
    usleep(100);
    pthread_mutex_lock(&mutex1);
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex3);
}

int main(int argc,const char* argv[])
{
    
    
    // 创建三个线程
    pthread_t tid1,tid2,tid3;
    pthread_create(&tid1,NULL,run1,NULL);
    pthread_create(&tid2,NULL,run2,NULL);
    pthread_create(&tid3,NULL,run3,NULL);
    // 主线程等待三个子线程结束
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    return 0;
}

Cómo evitar interbloqueos:

Si solo una de las cuatro condiciones que constituyen un interbloqueo no es verdadera, no se producirá un interbloqueo.

1. Destruir las condiciones mutuamente excluyentes para que los recursos puedan ser compartidos (preparar copias múltiples).

2. Destruya las condiciones de posesión y espera, solicite todos los recursos que necesita al mismo tiempo, no lo deje funcionar hasta que los recursos no estén satisfechos, y una vez que comience a funcionar, siempre será de su propiedad. La desventaja es que los recursos del sistema se desperdiciarán.

3. Rompe la condición de inalienable. Cuando ya ocupas algunos recursos y solicitas nuevos recursos pero no puedes conseguirlos, liberarás los recursos que ya has obtenido. La desventaja es que es más complicado de implementar, y liberar los recursos que ya tienes. obtenido puede causar problemas previos Una etapa de trabajo desperdiciada.

4. Destruya la condición de espera circular, adopte el método de asignación secuencial de recursos, numere los recursos en el sistema, estipule que los subprocesos deben obtener recursos en orden creciente, la desventaja es que los recursos deben ser relativamente estables, lo que limita el aumento y disminución de los recursos.

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

// 创建三个互斥锁并初始化
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;

void* run1(void* arg)
{
    
    
	while(1)
	{
    
    
        pthread_mutex_lock(&mutex1);
        usleep(100);
        if(0 == pthread_mutex_trylock(&mutex2))
        break;
        pthread_mutex_unlock(&mutex1);
    }
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
}

void* run2(void* arg)	
{
    
    
	while(1)
	{
    
    
        pthread_mutex_lock(&mutex2);
        usleep(100);
        if(0 == pthread_mutex_trylock(&mutex3))
        break;
        pthread_mutex_unlock(&mutex2);
    }
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex3);
    pthread_mutex_unlock(&mutex2);
}

void* run3(void* arg)
{
    
    
	while(1)
	{
    
    
        pthread_mutex_lock(&mutex3);
        usleep(100);
        if(0 == pthread_mutex_trylock(&mutex1))
        break;
        pthread_mutex_unlock(&mutex3);
	}
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex3);
}

int main(int argc,const char* argv[])
{
    
    
    // 创建三个线程
    pthread_t tid1,tid2,tid3;
    pthread_create(&tid1,NULL,run1,NULL);
    pthread_create(&tid2,NULL,run2,NULL);
    pthread_create(&tid3,NULL,run3,NULL);
    // 主线程等待三个子线程结束
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    return 0;
}

Formas de detectar interbloqueos:

Idea general: observación + análisis

Método 1: Lea el código y analice los pasos de bloqueo de cada subproceso.

Método 2: use strace para rastrear el flujo de ejecución del programa.

Método 3: Ver el proceso de ejecución empresarial del observador de registros.

Método 4: use gdb para depurar y verificar el estado de ejecución de cada subproceso.

1、把断点打在线程创建完毕后
2、run
3、info threads 查看所有线程
4、thread n 进程指定的线程
5、bt 查看线程堆栈信息
6、配合s/n单步调试

5. Operaciones atómicas

La llamada operación atómica es una operación que no se puede dividir. Para operaciones de subprocesos múltiples en variables globales, ya no hay necesidad de bloqueos de subprocesos. Tiene el mismo efecto protector que pthread_mutex_t, y también es seguro para subprocesos. Algunos los compiladores lo usan cuando se necesita agregar los parámetros de compilación -march=i686.

type __sync_fetch_and_add (type *ptr, type value); // +
type __sync_fetch_and_sub (type *ptr, type value); // -
type __sync_fetch_and_and (type *ptr, type value); // &
type __sync_fetch_and_or (type *ptr, type value); // |
type __sync_fetch_and_nand (type *ptr, type value); // ~
type __sync_fetch_and_xor (type *ptr, type value); // ^
功能:以上操作返回的是*ptr的旧值
    
type __sync_add_and_fetch (type *ptr, type value); // +
type __sync_sub_and_fetch (type *ptr, type value); // -
type __sync_and_and_fetch (type *ptr, type value); // &
type __sync_or_and_fetch (type *ptr, type value); // |
type __sync_nand_and_fetch (type *ptr, type value); // ~
type __sync_xor_and_fetch (type *ptr, type value); // ^
功能:以上操作返回的是*ptr与value计算后的值
    
type __sync_lock_test_and_set (type *ptr, type value);
功能:把value赋值给*ptr,并返回*ptr的旧值
    
__sync_lock_release(type *ptr);
功能:将*ptr赋值为0
#include <stdio.h>
#include <pthread.h>

int num = 0;
void* run(void* arg)
{
    
    
    for(int i=0; i<100000000; i++)
    {
    
    
        __sync_fetch_and_add(&num,1);
    }
}
int main(int argc,const char* argv[])
{
    
    
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,run,NULL);
    pthread_create(&pid2,NULL,run,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    printf("%d\n",num);
}

Ventajas de las operaciones atómicas:

1. La velocidad es extremadamente rápida

2. No habrá punto muerto

Desventajas de las operaciones atómicas:

1. Esta función no es universal y algunos compiladores no la admiten.

2. type solo puede ser un tipo relacionado con enteros, y no se pueden usar tipos de punto flotante ni personalizados.

6. Modelo de productor y consumidor

Productor: el subproceso que produce datos.Este tipo de subproceso es responsable de recibir datos del cliente y del cliente, y luego enviar los datos al intermediario de almacenamiento.

Consumidor: el subproceso responsable de consumir datos y procesar (juzgar, filtrar, usar, responder, almacenar) los datos producidos por el subproceso productor.
Intermediario de almacenamiento: también llamado almacén de datos, es un búfer de datos entre el hilo del productor y el hilo del consumidor, que se utiliza para equilibrar la velocidad de producción desequilibrada y la velocidad de consumo entre los dos, y aísla a los productores y consumidores a través del búfer, en comparación con la conexión directa entre los dos, evita esperar el uno al otro y mejora la eficiencia operativa.

Problema 1: la producción es más rápida que el consumo, el búfer está lleno y está agotado.

Solución: El subproceso a cargo de la producción notifica al subproceso a cargo del consumo que consuma a toda velocidad y luego se vaya a dormir.

Problema 2: Consumir más rápido que producir, buffer vacío, inanición.

Solución: El subproceso a cargo del consumo informa al subproceso a cargo de la producción para producir a toda velocidad y luego se va a dormir.

7. Variables de condición

Las variables de condición son un mecanismo de sincronización utilizando "variables globales" compartidas entre subprocesos, que incluyen principalmente dos acciones:

1. El subproceso espera a que "se establezca la condición de la variable de condición" y duerme;

2. Espere a que se establezca la "condición" para activar el subproceso inactivo.

Para evitar condiciones de carrera, el uso de variables de condición siempre se combina con una exclusión mutua.

int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr);
//亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// 使调用线程睡入条件变量cond,同时释放互斥锁mutex
int pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex);

int pthread_cond_timedwait (pthread_cond_t* cond,
    pthread_mutex_t* mutex,
    const struct timespec* abstime);

struct timespec {
    
    
    time_t tv_sec; // Seconds
    long tv_nsec; // Nanoseconds [0 - 999999999]
};

// 从条件变量cond中唤出一个线程,
// 令其重新获得原先的互斥锁
int pthread_cond_signal (pthread_cond_t* cond);

注意:被唤出的线程此刻将从pthread_cond_wait函数中返回,
但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。
    
// 从条件变量cond中唤醒所有线程
int pthread_cond_broadcast (pthread_cond_t* cond);

// 销毁条件变量
int pthread_cond_destroy (pthread_cond_t* cond);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
// 仓库最大容量
#define STACK_MAX 50

// 仓库
char stack[STACK_MAX];
// 仓库的入口和出口
int top;

// 保护仓库入口的互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 当仓库满时线程睡入的条件变量
pthread_cond_t full = PTHREAD_COND_INITIALIZER;
// 当仓库空时线程睡入的条件变量
pthread_cond_t empty = PTHREAD_COND_INITIALIZER;

// 显示仓库内容
void show_stack(const char* opt,char data)
{
    
    
    if(50 < top || 0 > top)
    {
    
    
        printf("爆仓了 %d\n",top);
        exit(0);
    }
    for(int i=0; i<top; i++)
    {
    
    
        printf("%c",stack[i]);
    }
    printf("%s%c\n",opt,data);
}
// 生产者
void* production(void* arg)
{
    
    
    for(;;)
    {
    
    
        // 往仓库中push数据,加锁保护入口
        pthread_mutex_lock(&mutex);
        // 发现仓库满了
        while(top >= STACK_MAX)
        {
    
    
            // 叫睡所有消费线程全速消费,虽然暂时只能醒来一个消费者线程,但所有消费线程已经加
            入到争夺互斥锁的过程
            pthread_cond_broadcast(&empty);
            // 生产者线程睡入满仓条件变量
            pthread_cond_wait(&full,&mutex);
        }
        
        // 生产数据
        char data = rand()%26+'A';
        // 显示仓库
        show_stack("<-",data);
        // 数据添加到仓库
        stack[top++] = data;
        // 模拟生产数据消耗的时间
        usleep(rand()%5*10000);
        
        // 此时仓库已经不空了,叫醒一个空仓条件休眠的消费者线程
        pthread_cond_signal(&empty);
        
        // 生产完毕解锁入口
        pthread_mutex_unlock(&mutex);
    }
}

// 消费者
void* consumption(void* arg)
{
    
    
    for(;;)
    {
    
    
        // 从仓库中消费数据,加锁保护出口
        pthread_mutex_lock(&mutex);
        // 发现空仓
        while(0 == top)
        {
    
    
            // 叫醒所有生产线程全速生产,虽然暂时只能醒来一个生产者线程,但所有生产者线程都加
            入到争夺互斥锁的过程
            pthread_cond_broadcast(&full);
            // 消费者线程睡入空仓条件变量
            pthread_cond_wait(&empty,&mutex);
        }
        
        // 从仓库中消费数据
        char data = stack[top--];
        // 显示仓库内容
        show_stack("->",data);
        // 模拟消费数据所消耗的时间
        usleep(rand()%5*10000);
        
        // 此时已经不满,叫醒一个因为满仓而休眠的线程
        pthread_cond_signal(&full);
        
        // 消费完毕解锁出口
        pthread_mutex_unlock(&mutex);
    }
}

int main(int argc,const char* argv[])
{
    
    
    srand(time(NULL));
    
    pthread_t tids[10];
    for(int i=0; i<10; i++)
    {
    
    
        if(i%2)
        	pthread_create(tids+i,NULL,production,NULL);
        else
        	pthread_create(tids+i,NULL,consumption,NULL);
    }
    for(int i=0; i<10; i++)
    {
    
    
        pthread_join(tids[i],NULL);
    }
    return 0;
}

ocho, semáforo

Semáforos utilizados por varios subprocesos:

#include <semaphore.h>
sem_t sem;

int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:给信号量设置初始值
pshared:信号量的使用范围
	0 线程间使用
	nonzero 进程之间使用
    
int sem_wait(sem_t *sem);
功能:信号量减1操作,如果信号量已经等于0,则阻塞
    
int sem_trywait(sem_t *sem);
功能:尝试对信号量减1操作,能减返回0成功,不能减返回-1失败,不会阻塞
    
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
功能:带倒计时的对信号减1操作,能减返回0成功,不能减超时返回-1失败,阻塞abs_timeout一段时间
    
int sem_post(sem_t *sem);
功能:对信号量执行加1操作
    
int sem_getvalue(sem_t *sem, int *sval);
功能:获取信号量的值
    
int sem_destroy(sem_t *sem);
功能:销毁信号量

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

// 仓库最大容量
#define STACK_MAX 50

// 仓库
char stack[STACK_MAX];
// 仓库的入口和出口
int top;

// 定义信号量
sem_t data_sem,null_sem;

// 显示仓库内容
void show_stack(const char* opt,char data)
{
    
    
    if(50 < top || 0 > top)
    {
    
    
        printf("爆仓了 %d\n",top);
        exit(0);
    }
    for(int i=0; i<top; i++)
    {
    
    
    	printf("%c",stack[i]);
    }
   	 printf("%s%c\n",opt,data);
}

// 生产者
void* production(void* arg)
{
    
    
    for(;;)
    {
    
    
        // 空位置的数量减1
        sem_wait(&null_sem);
        // 生产数据
        char data = rand()%26+'A';
        // 显示仓库
        show_stack("<-",data);
        // 数据添加到仓库
        stack[top] = data;
        __sync_fetch_and_add(&top,1);
        usleep(rand()%100*100);
        // 数量的数量加1
        sem_post(&data_sem);
    }
}

// 消费者
void* consumption(void* arg)
{
    
    
    for(;;)
    {
    
    
        // 数据的数量减1
        sem_wait(&data_sem);
        // 从仓库中消费数据
        char data = stack[top];
        __sync_fetch_and_sub(&top,1);
        // 显示仓库内容
        show_stack("->",data);
        // 模拟消费数据所消耗的时间
        usleep(rand()%1000*10);
        // 空位置的数量加1
        sem_post(&null_sem);
    }
}

int main(int argc,const char* argv[])
{
    
    
    srand(time(NULL));
    
    // 初始化空位置的数量
    sem_init(&null_sem,0,STACK_MAX);
    // 初始化数据的数量
    sem_init(&data_sem,0,0);
    
    pthread_t tids[10];
    for(int i=0; i<10; i++)
    {
    
    
        if(i<5)
       		pthread_create(tids+i,NULL,production,NULL);
        else
       		pthread_create(tids+i,NULL,consumption,NULL);
    }
    
    for(int i=0; i<10; i++)
    {
    
    
        pthread_join(tids[i],NULL);
    }
    
    return 0;
}

Semáforos utilizados por múltiples procesos:

sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
功能:在内核创建一个信号量对象
name:信号量的名字
oflag:
    O_CREAT 不存在则创建信号量,存在则获取
    O_EXCL 如果信号量已经存在,返回失败
mode:信号量的权限
value:信号量的初始值
    
sem_t *sem_open(const char *name, int oflag);
功能:获取信号,或相关属性
    
int sem_unlink(const char *name);
功能:删除信号量

// 进程A
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

int main(int argc,const char* argv[])
{
    
    
    sem_t* sem = sem_open("sem_name",O_CREAT,0644,0);
    for(;;)
    {
    
    
        sem_post(sem);
        sleep(1);
        int value = 0;
        sem_getvalue(sem,&value);
        printf("%d\n",value);
	}
    
	return 0;
}

// 进程B
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

int main(int argc,const char* argv[])
{
    
    
    sem_t* sem = sem_open("sem_name",O_CREAT);
    for(;;)
    {
    
    
        sem_wait(sem);
        sleep(2);
        int value = 0;
        sem_getvalue(sem,&value);
        printf("%d\n",value);
    }
    
    return 0;
}

Supongo que te gusta

Origin blog.csdn.net/m0_62480610/article/details/127553447
Recomendado
Clasificación