【linux】线程条件变量 信号量

本文介绍了Linux中线程条件变量和信号量的概念及应用。条件变量通常与互斥锁配合,提供多线程会合的场所,涉及pthread_cond_系列函数。信号量作为一种同步机制,能在保证数据同步的同时提高线程并发性,包括sem_系列函数。文中通过生产-消费者模型展示了条件变量的使用,并对比了互斥锁的粒度问题,强调信号量在部分数据共享中的优势。

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

1.条件变量

        条件变量本身不是锁!但它也可以造成线程阻塞

        通常与互斥锁配合使用。给多线程提供一个会合的场所。

        1.主要函数应用

                pthread_cond_init函数

                pthread_cond_destroy函数

                pthread_cond_wait函数

                pthread_cond_timedwait函数

                pthread_cond_signal函数

                pthread_cond_broadcast函数

以上6 个函数的返回值都是,成功返回0,失败直接返回错误号

                pthread_cond_t类型用于定义条件变量

                pthread_cond_t cond;

        pthread_cond_init函数    初始化一个条件变量                

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr *restrict attr);
参1:需要初始化的条件变量
参2:arrt表条件比那里属性,通常为默认值,传NULL即可
         初始化有两种方法
         1.动态初始化:pthread_cond_init(&cond,NULL)
         2.静态初始化: pthread_cond_t cond = PTHREAD_COND_INITIALIZER ;
          pthread_cond_wait函数    阻塞等待一个条件变量
        
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
参1:已经初始化号的条件变量,如果条件不满足,则一直阻塞等待
参2:一把已加锁的互斥锁,解锁已加锁的mutex,等价pthread_mutex_unlock(&mutex)
        参1、参2执行时是原子操作。(也就阻塞时,会继续操作解锁,中途不会被中断)
函数返回return前:
        当条件必须满足,同步解除阻塞并重新给互斥量加pthread_mutex_lock(&mutex)
        pthread_cond_signal函数
                唤醒等待在该条件变量上的一个线程
        pthread_cond_broadcast函数
                唤醒等待在带条件变量上的所有线程
         int pthread_cond_signal(&cong);
           参1:已经初始化好的条件变量
2.生产-消费者模型
        

 例程:生产-消费者模型代码

#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
/*链表作为公享数据,需被互斥量保护*/
#define SIZE 100
int repository[SIZE]={0};//定义仓库大小,并初始化为0
/*静态初始化一个条件变量和一个互斥量*/
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int empty_repository(int arr[])
{
int i,sum=0;
for(i=0;i<SIZE;i++)
{
sum += arr[i];
}
return sum;
}
int fill_repository(int arr[])
{
int i,sum;
for(i=0;i<SIZE;i++)
{
if(arr[i]==0)
{
arr[i]= rand() % 1000 + 1;
printf("-------fill a[%d]=%d\n",i,arr[i]);
return arr[i];
}
}
return -1;
}
int del_repository(int arr[])
{
int i,temp;
for(i=SIZE;i>=0;i--)
{
if(arr[i]!=0)
{
temp=arr[i];
printf("-------del a[%d]=%d\n",i,arr[i]);
arr[i]= 0;
return temp;
}
}
return -1;
}
void *consumer(void *p)
{
for (;;)
{
int ret=pthread_mutex_lock(&lock);
if(ret !=0)
{
printf("consumer mutex_lock err:%s\n",strerror(ret));
}
while (!empty_repository(repository))
{
//如果条件不满足,释放锁,并阻塞在此处
//如果条件满足,重新加锁,并解除阻塞,进行循环条件判断
printf("cond_wait test mask\n");
pthread_cond_wait(&has_product, &lock);
}
//模拟消费掉一个产品
ret=del_repository(repository);
if(ret==-1)
{
printf("del_repository() err\n");
pthread_exit(NULL);
}
pthread_mutex_unlock(&lock);
printf("-Consume %lu---Produce id=%d\n", pthread_self(), ret);
sleep(rand() % 5);
}
}
void *producer(void *p)
{
for (; ;)
{ //sleep(5),为验证consumer线程中的,pthread_cond_wait()会进行解锁+阻塞功能
sleep(5);
//生产者拿锁成功,再生产产品
int ret=pthread_mutex_lock(&lock);
if(ret !=0)
{
printf("producer mutex_lock() err:%s\n",strerror(ret));
pthread_mutex_unlock(&lock);
break;//跳出循环
}
//模拟生产一个产品
ret=fill_repository(repository);
if(ret==-1)
{
printf("fill_repository() err\n");
pthread_mutex_unlock(&lock);
break;
// pthread_exit(NULL);//生产仓库满后,无法继续生产,退出生产线程
}
pthread_mutex_unlock(&lock);
printf("-producer %lu---Produce id=%d\n", pthread_self(), ret);
pthread_cond_signal(&has_product); //条件满足了,通知等待条件变量has_product的
线程
usleep(100000);
}
}
int main(int argc, char *argv[])
{
pthread_t tid01, tid02,tid03;
srand(time(NULL));
pthread_create(&tid01, NULL, producer, NULL);
pthread_create(&tid02, NULL, consumer, NULL);
// pthread_create(&tid03, NULL, consumer, NULL);
pthread_join(tid01, NULL);
pthread_join(tid02, NULL);
// pthread_join(tid03, NULL);
return 0;
}

 2.信号量

        信号量是相对折中的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发。

由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据锁住。这样的话虽然达到了多线程操作共享数据时保证数据正确性的目的,却无形中导致线程的并发性下降。线程从并行执行,变成了串行执行。与直接 使用单进程无异。

        主要函数

        sem_init函数

        sem_destroy函数

        sem_wait函数

        sem_trywait函数

        sem_timedwait函数

        sem_post函数

        以上6个函数的返回值都是:成功返回0,失败返回-1,同时设置errno(注意,他们没有pthread)前缀

                 sem_t类型,本质仍是结构体。但应用期间可简单看作为整数,忽略实现细节(类似于使用文件描述符)。

                sem_tsem规定信号量sem不能<0     头文件<semaphore.h>

        信号的基本操作

                sem_wait:1.信号量大于0,则信号量-- (类比pthread_mutex_lock)

                                  2. 信号量等于 0 时,再次调用会造成线程阻塞。
                对应:
                        
                        sem_post : 将信号量++,同时唤醒阻塞在信号量上的线程 ( pthread_mutex_unlock)
但,由于 sem 的实现对用户隐藏,所以所谓的 ++. -- 操作只能通过函数来实现,而不能直接 ++ - 符号 信号量的初值,决定了占用信号量的线程的个数。
        例程:生产-消费者模型,使用信号量
                   
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>

#define NUM 5

int queue[NUM]; //全局数组实现环形队列
sem_t blank_number,product_number; //空格子信号量,产品信号量

void *producer(void *arg)
{
	int i = 0;
	while(1)
	{
		sem_wait(&blank_number); //生产者将空格子数--,为0则阻塞等待
		queue[i] = rand() % 1000 + 1;//生产一个产品
		printf("----produce-----%d\n",queue[i]);
		sem_post(&product_number); //将产品数++

		i = (i+1)%NUM;//借助下标实现环形
		sleep(rand()%1);
	}
}

void *consumer(void *arg)
{
	int i=0;
	while(1)
	{
		sem_wait(&product_number);//消费者将产品数--,为则阻塞等待
		printf("---consume-----%d\n",queue[i]);
		queue[i] = 0;//消费一个产品
		sem_post(&blank_number);//消费掉以后,将空格子数++

		i = (i+1) % NUM;
			sleep(rand()%3);
	}
}

int main(int argc,char *argv[])
{
	pthread_t pid,cid;
	sem_init(&blank_number,0,NUM);//初始化线程间共享-0,空格子信号量为5
	sem_init(&product_number,0,0);//初始化线程间共享-0,产品数为0

	pthread_create(&pid,NULL,producer,NULL);
	pthread_create(&cid,NULL,consumer,NULL);

	pthread_join(pid,NULL);
	pthread_join(cid,NULL);

	sem_destroy(&blank_number);
	sem_destroy(&product_number);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值