信号变量/信号量

本文介绍了信号变量与信号量在多线程编程中的应用。信号变量通常与互斥锁配合,用于线程间的同步和条件等待。而信号量则是一种更强大的同步机制,可以理解为加强版的互斥锁。文中详细阐述了条件变量的初始化、等待、唤醒操作以及信号量的初始化、加减、尝试加锁和销毁等函数的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

信号变量

条件本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。

使用互斥量保护共享数据
使用条件变量可以使线程阻塞, 等待某个条件的发生, 当条件满足的时候解除阻塞

条件变量的两个动作:

条件不满足,线程阻塞
条件满足,通知阻塞的线程解除阻塞, 开始工作。

条件变量相关函数

pthread_cond_t cond;
定义一个条件变量

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
函数描述:
    初始化条件变量
函数参数
    cond:条件变量
    attr:条件变量属性, 通常传NULL
函数返回值:成功返回0,错误返回错误号

int pthread_cond_destroy(pthread_cond_t *cond);
    销毁条件变量

int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
函数描述:
    条件不满足,引起线程阻塞并解锁
    条件满足,解除线程阻塞,并加锁
函数参数:
     cond: 条件变量
    mutex: 互斥锁变量
函数返回值:成功返回0,错误返回错误号

int pthread_cond_signal(pthread_cond_t *cond);
函数描述:
    唤醒至少一个阻塞在该条件变量上的线程
函数参数:
    条件变量–>cond
函数返回值: 成功返回0, 失败返回错误号

//使用条件变量实现生产者和消费者模型
/*
用链表模仿生产者和消费者
生产者:向链表中插入节点
消费者:从链表中读取节点,同时将该节点销毁
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct node
{
	int data;
	struct node *next;
}NODE;

NODE *head = NULL;

//定义一把锁
pthread_mutex_t mutex;

//定义条件变量
pthread_cond_t cond;

//生产者线程
void *producer(void *arg)
{
	NODE *pNode = NULL;
	while(1)
	{
		//生产一个节点
		pNode = (NODE *)malloc(sizeof(NODE));
		if(pNode==NULL)
		{
			perror("malloc error");
			exit(-1);
		}
		pNode->data = rand()%1000;
		printf("P:[%d]\n", pNode->data);

		//加锁
		pthread_mutex_lock(&mutex);

		pNode->next = head;
		head = pNode;

		//解锁
		pthread_mutex_unlock(&mutex);

		//通知消费者线程解除阻塞
		pthread_cond_signal(&cond);
		
		sleep(rand()%3);
	}
}


//消费者线程
void *consumer(void *arg)
{
	NODE *pNode = NULL;
	while(1)
	{
		//加锁
        pthread_mutex_lock(&mutex);
		
		if(head==NULL)
		{
			//若条件不满足,需要阻塞等待
			//若条件不满足,则阻塞等待并解锁;
			//若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁 
			pthread_cond_wait(&cond, &mutex);
		}

		printf("C:[%d]\n", head->data);	
		pNode = head;
		head = head->next;

		//解锁
		pthread_mutex_unlock(&mutex);

		free(pNode);
		pNode = NULL;

		sleep(rand()%3);
	}
}

int main()
{
	int ret;
	pthread_t thread1;
	pthread_t thread2;

	//初始化互斥锁
	pthread_mutex_init(&mutex, NULL);

	//条件变量初始化
	pthread_cond_init(&cond, NULL);

	//创建生产者线程
	ret = pthread_create(&thread1, NULL, producer, NULL);
	if(ret!=0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//创建消费者线程
	ret = pthread_create(&thread2, NULL, consumer, NULL);
	if(ret!=0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//等待线程结束
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);

	//释放互斥锁
	pthread_mutex_destroy(&mutex);

	//释放条件变量
	pthread_cond_destroy(&cond);
	return 0;
}

在这里插入图片描述


多个生成者和多个消费者程序在执行的时候core掉的原因分析:
	假若只有一个生产者生产了一个节点, 此时会调用pthread_cond_signal通知
	消费者线程, 此时若有多个消费者被唤醒了, 则最终只有消费者获得锁, 然后进行
	消费, 此时会将head置为NULL, 然后其余的几个消费者线程只会有一个线程获得锁, 
	然后读取head的内容就会core掉.

	在使用条件变量的线程中, 能够引起线程的阻塞的地方有两个:
		1 在条件变量处引起阻塞---->这个阻塞会被pthread_cond_signal解除阻塞
		2 互斥锁也会使线程引起阻塞----->其他线程解锁会使该线程解除阻塞.

读写锁使用步骤:
1 先定义一把读写锁:
    pthread_rwlock_t rwlock;
2 初始化读写锁
    pthread_rwlock_init(&rwlock, NULL);
3 加锁
    pthread_rwlock_rdlock(&rwlock);---->加读锁
    pthread_rwlock_wrlock(&rwlock);---->加写锁

共享资源出现的位置
/
4 解锁
     pthread_rwlock_unlock(&rwlock);
5 释放锁
     pthread_rwlock_destroy(&rwlock);

信号量

信号量相当于多把锁, 可以理解为是加强版的互斥锁

相关函数

  • 定义信号量 sem_t sem;
  • int sem_init(sem_t *sem, int pshared, unsigned int value);
        函数描述: 初始化信号量
        函数参数:
            sem: 信号量变量
            pshared: 0表示线程同步, 1表示进程同步
            value: 最多有几个线程操作共享数据
        函数返回值:成功返回0, 失败返回-1, 并设置errno值
  • int sem_wait(sem_t *sem);
        函数描述: 调用该函数一次, 相当于sem–, 当sem为0的时候, 引起阻塞
        函数参数: 信号量变量
        函数返回值: 成功返回0, 失败返回-1, 并设置errno值
  • int sem_post(sem_t *sem);
        函数描述: 调用一次, 相当于sem++
        函数参数: 信号量变量
        函数返回值: 成功返回0, 失败返回-1, 并设置errno值
  • int sem_trywait(sem_t *sem);
        函数描述: 尝试加锁, 若失败直接返回, 不阻塞
        函数参数: 信号量变量
        函数返回值: 成功返回0, 失败返回-1, 并设置errno值
  • int sem_destroy(sem_t *sem);
        函数描述: 销毁信号量
        函数参数: 信号量变量
        函数返回值: 成功返回0, 失败返回-1, 并设置errno值

在这里插入图片描述

信号量实现步骤:
1 定义信号量变量
    sem_t sem1;
    sem_t sem2;
2 初始化信号量
    sem_init(&sem1, 0, 5);
    sem_init(&sem2, 0, 5);
3 加锁
    sem_wait(&sem1);
//共享资源
    sem_post(&sem2);
-------------------------------------
    sem_wait(&sem2);
//共享资源
    sem_post(&sem1);
4 释放资源
    sem_destroy(sem1);
    sem_destroy(sem2);

//使用信号量实现生产者和消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
typedef struct node
{
	int data;
	struct node *next;
}NODE;

NODE *head = NULL;

//定义信号量
sem_t sem_producer;
sem_t sem_consumer;

//生产者线程
void *producer(void *arg)
{
	NODE *pNode = NULL;
	while(1)
	{
		//生产一个节点
		pNode = (NODE *)malloc(sizeof(NODE));
		if(pNode==NULL)
		{
			perror("malloc error");
			exit(-1);
		}
		pNode->data = rand()%1000;
		printf("P:[%d]\n", pNode->data);

		//加锁
		sem_wait(&sem_producer); //--

		pNode->next = head;
		head = pNode;

		//解锁
		sem_post(&sem_consumer);  //相当于++

		sleep(rand()%3);
	}
}

//消费者线程
void *consumer(void *arg)
{
	NODE *pNode = NULL;
	while(1)
	{
		//加锁
		sem_wait(&sem_consumer); //相当于--
		
		printf("C:[%d]\n", head->data);	
		pNode = head;
		head = head->next;

		//解锁
		sem_post(&sem_producer); //相当于++

		free(pNode);
		pNode = NULL;

		sleep(rand()%3);
	}
}

int main()
{
	int ret;
	pthread_t thread1;
	pthread_t thread2;

	//初始化信号量
	sem_init(&sem_producer, 0, 5);
	sem_init(&sem_consumer, 0, 0);

	//创建生产者线程
	ret = pthread_create(&thread1, NULL, producer, NULL);
	if(ret!=0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//创建消费者线程
	ret = pthread_create(&thread2, NULL, consumer, NULL);
	if(ret!=0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//等待线程结束
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);

	//释放信号量资源
	sem_destroy(&sem_producer);
	sem_destroy(&sem_consumer);

	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值