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

news/2024/7/7 21:01:06

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;
}


http://lihuaxi.xjx100.cn/news/125953.html

相关文章

01.高等数学基础

高等数学基础1.数列2. 极限2.1 符号表示2.2 极限充要条件2.3 极限中的无穷小2.4 极限中的无穷大2.5 无穷小的比较3.函数的连续性3.1 函数连续性定义3.2 函数连续性满足的条件3.3 函数的间断点3.4 函数间断点分类4.导数4.1 导数定义4.2 常用的导数4.3导数基本运算5.偏导数5.1 偏…

处理多维特征的输入

一、多维特征输入 多维数据的输入可以看成矩阵进行运算&#xff0c;如下有8维的数据&#xff08;N*8&#xff09;&#xff0c;进变换后形成1维的数据&#xff08;N*1&#xff09; 二、糖尿病数据集为例 数据说明 pregnancies&#xff1a; 怀孕次数 glucose&#xff1a;口服葡…

【每天学习一点新知识】CC攻击和DDoS的区别

目录 CC攻击原理 CC攻击现象&#xff1a; 和DDoS的区别 CC攻击的常用防护方式 CC攻击原理 CC&#xff08;Challenge Collapsar&#xff09;&#xff0c;可以归为DDoS攻击的一种&#xff0c;对一些消耗资源较大的应用页面不断发起正常的请求&#xff0c;以达到消耗服务端资源…

两个List循环效率对比 List转Map 循环效率对比 Listmap 循环 效率对比

两个List循环效率对比 List转Map 循环效率对比 Listmap 循环 效率对比 一、情景描述 1、在微服务开发中&#xff0c;如&#xff1a; 查询用户列表 userList&#xff0c;需要关联查询 每个用户下面的文件信息&#xff0c;由于数据库层隔离&#xff0c;不能直接进行 left join &a…

Endnote X9文献管理器应用---使用总结

Endnote文献管理器应用---使用总结1. 文献分类和文献管理器Endnote2. Endnote使用&#xff08;1&#xff09;新建本地文献库&#xff08;2&#xff09;创建分组和文献导入&#xff08;3&#xff09;文献插入word文档&#xff08;4&#xff09;文献Style编辑&#xff0c;下载&am…

安装 Windows 7 VM虚拟机

目录&#xff08;1&#xff09;选择系统安装语言&#xff0c;时间与格式&#xff0c;键盘格式&#xff08;2&#xff09;点击【Install now】&#xff08;3&#xff09;选择Windows 10系统的具体版本&#xff08;4&#xff09;同意【Applicable notices license terms】&#x…

盘点多边形战士Polygon有哪些扩容解决方案|Tokenview

加密行业里&#xff0c;以太坊扩容问题的解决方案一直是个巨大的市场。多边形战士Polygon则一直以来致力于解决以太坊的扩容问题。什么是Polygon&#xff1f;Polygon是以太坊的layer2扩容方案&#xff0c;通过提供一种通用框架来创建与以太坊相兼容的扩容解决方案&#xff0c;旗…

[CSP-S 2022] 策略游戏 题解

[CSP-S 2022] 策略游戏 题解题面题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1样例 #2样例输入 #2样例输出 #2提示解析小 Q 的选择小 L 的选择总结代码题面 题目描述 小 L 和小 Q 在玩一个策略游戏。 有一个长度为 nnn 的数组 AAA 和一个长度为 mmm 的数组 BBB&am…