当前位置: 首页 > news >正文

从信号量开始的里牛渴死生活

讲讲信号量

POSIX信号量

这个曾经在进程间通信提过一嘴但是没怎么细说,POSIX信号量和SystemV信号量都可用于同步达到无冲突的访问共享资源的目的,POSIX还可以用于线程间同步

初始化
#include <semaphore.h> 
int sem_init(sem_t *sem, int pshared, unsigned int value); 

参数: pshared:0表示线程间共享,非零表示进程间共享

value:信号量初始值  

销毁
int sem_destroy(sem_t *sem);
等待
int sem_wait(sem_t *sem); //P() 

这是等待信号量,会将信号量的值-1 

发布
int sem_post(sem_t *sem);//V() 

这是发布信号量,资源使用完毕,可以归还了,信号量值+1 

环形队列

环形队列也是队列,也要遵守先进先出,哎哟但是我好像就这个没学啊我去

今天晚些时候学吧

逻辑是环状的,但是物理上是线性的,是通过取模运算模拟出来的

当环形队列为空或者为满的时候,head == end

我们需要一个计数器或者牺牲一个空位置区分空和满

我们如今的问题是多线程如何在环形队列中进行生产和消费

那么多线程如何在环形队列中生产和消费?

队列为空的时候让谁先访问呢?

肯定是让生产者先生产啊

队列为满的时候让谁访问?

肯定是让消费者来消费啊

在局部内访问资源有顺序性

大部分情况下这东西

不怎么出现

这样生产和消费会同时进行

我们的结论:

我们不能让生产者把消费者套一个圈

用信号量就能做到勒

消费者关心的资源是什么捏?

数据资源

生产者关心的什么资源?

空间

二者关心的东西不一样,相加为N

 我们可以定义这两个东西,一个是sem_t data_sem=0(开始的时候)

第二个我们叫空间信号量,是sem_t space_sem=N

要有生产者和消费者

写接口,这是RunQueue.hpp:

#pragma once#include<iostream>
#include<vector>
#include<string>
#include<semaphore.h>template<typename T>
class RingQueue
{
private:void P(sem_t &s){sem_wait(&s);}void V(sem_t &s){sem_post(&s);}
public:RingQueue(int max_cap):_max_cpp(max_cap),_ringqueue(max_cap),_c_step(0),_p_step(0){sem_init(&_data_sem,0,0);sem_init(&_space_sem,0,max_cap);}void Push(const T &in)      //生产者{P(_space_sem);_ringqueue[_p_step]=in;_p_step++;_p_step%=_max_cpp;  //因为是环形队列V(_data_sem);    //完成生产要释放资源}void Pop(T* out)            //消费者{P(_data_sem);*out = _ringqueue[_c_step];_c_step++;_c_step%=_max_cpp;V(_space_sem);}~RingQueue(){sem_destroy(&_data_sem);sem_destroy(&_space_sem);}
private:std::vector<T> _ringqueue;int _max_cpp;int _c_step;int _p_step;sem_t _data_sem;    //消费者关心sem_t _space_sem;   //生产者关心
};

用了一些手段 

这是Main.cc:

#pragma once#include"RunQueue.hpp"
#include<iostream>
#include<unistd.h>
#include<string>
#include<pthread.h>void *Consumer(void* args)
{RingQueue<int> *rq = static_cast<RingQueue<int>*>(args);while (true){sleep(1);int data = 0;//消费rq->Pop(&data);//处理数据std::cout << "Consumer -> " << data << std::endl;}
}void* Productor(void* args)
{RingQueue<int> *rq = static_cast<RingQueue<int>*>(args);while (true){sleep(1);//构造数据int data = rand() % 10 + 1;//生产rq->Push(data);std::cout << "Productor -> " << data << std::endl;}
}int main()
{srand(time(nullptr)^getgid());RingQueue<int> *rq = new RingQueue<int>(5);pthread_t c,p;pthread_create(&c,nullptr,Consumer,rq);pthread_create(&p,nullptr,Productor,rq);pthread_join(c,nullptr);pthread_join(p,nullptr);return 0;
}

环形队列里也可以存放任务捏

Main.cc:

#pragma once#include"RunQueue.hpp"
#include"Task.hpp"
#include<iostream>
#include<unistd.h>
#include<string>
#include<pthread.h>void *Consumer(void* args)
{RingQueue<Task> *rq = static_cast<RingQueue<Task>*>(args);while (true){sleep(1);Task t;//消费rq->Pop(&t);//处理数据t();std::cout << "Consumer -> " << t.result() << std::endl;}
}void* Productor(void* args)
{RingQueue<Task> *rq = static_cast<RingQueue<Task>*>(args);while (true){sleep(1);//构造数据int x = rand() % 10 + 1;int y = rand()%10 + 1;Task t(x,y);//生产rq->Push(t);std::cout << "Productor -> " << t.debug() << std::endl;}
}int main()
{srand(time(nullptr)^getgid());RingQueue<Task> *rq = new RingQueue<Task>(5);pthread_t c,p;pthread_create(&c,nullptr,Consumer,rq);pthread_create(&p,nullptr,Productor,rq);pthread_join(c,nullptr);pthread_join(p,nullptr);return 0;
}

Task.hpp:

#pragma once#include <iostream>
#include <string>class Task 
{
public:Task() : x(0), y(0) {}Task(int a, int b) : x(a), y(b) {}// 执行任务,计算结果void operator()() {result_value = x + y; // 示例操作:计算 x 和 y 的和}// 返回结果int result() const {return result_value;}// 调试输出std::string debug() const {return "Task: x = " + std::to_string(x) + ", y = " + std::to_string(y);}private:int x; // 第一个参数int y; // 第二个参数int result_value; // 计算结果
};

 这是单生产单消费的情况,那么多生产多消费的情况应该怎么改呢?

有个问题,要不要加锁?

我们要维护生产者和消费者之间的互斥关系,所以需要加锁,多个生产者进来了生产者的下标位置只有一个,于是他们的下标位置就变成了临界资源,那么问题来了,要加几把锁?

要加两把锁

有个问题,加锁这件事情到底是在申请信号量之前还是之后捏?

答案已经很明晰了,,,

肯定是在之后捏

你这么想,信号量相当于电影票,肯定是人手先一张票之后再排队比较好啊,

这样效率比较高捏

#pragma once#include<iostream>
#include<vector>
#include<string>
#include<pthread.h>
#include<semaphore.h>template<typename T>
class RingQueue
{
private:void P(sem_t &s){sem_wait(&s);}void V(sem_t &s){sem_post(&s);}
public:RingQueue(int max_cap):_max_cpp(max_cap),_ringqueue(max_cap),_c_step(0),_p_step(0){sem_init(&_data_sem,0,0);sem_init(&_space_sem,0,max_cap);pthread_mutex_init(&_c_mutex,nullptr);pthread_mutex_init(&_p_mutex,nullptr);}void Push(const T &in)      //生产者{P(_space_sem);pthread_mutex_lock(&_p_mutex);          //加锁_ringqueue[_p_step]=in;_p_step++;_p_step%=_max_cpp;      //因为是环形队列V(_data_sem);           //完成生产要释放资源pthread_mutex_unlock(&_p_mutex);    //解锁}void Pop(T* out)            //消费者{P(_data_sem);pthread_mutex_lock(&_c_mutex);          //加锁*out = _ringqueue[_c_step];_c_step++;_c_step%=_max_cpp;V(_space_sem);pthread_mutex_unlock(&_c_mutex);        //解锁}~RingQueue(){sem_destroy(&_data_sem);sem_destroy(&_space_sem);pthread_mutex_destroy(&_c_mutex);pthread_mutex_destroy(&_p_mutex);}
private:std::vector<T> _ringqueue;int _max_cpp;int _c_step;int _p_step;sem_t _data_sem;        //消费者关心sem_t _space_sem;       //生产者关心pthread_mutex_t _c_mutex;pthread_mutex_t _p_mutex;
};

听着荷叶饭,我要开始升华了 

信号量对资源进行使用为什么不判断一下条件是否满足呢?

因为信号量本身就是判断条件

信号量:是一个计数器,是资源的预定机制,在外部可以不判断资源是否满足,就可以知道内部资源的情况

这种就是二元信号量,二元信号量等同于互斥锁

 多线程让我们能在处理数据和构造数据的过程中并发性高(这俩耗时比较长捏)

加几把锁啊好难啊我不想写了,,,

为什么我要当一个程序猿,我现在确实天天尖叫

世界真是魔幻啊

我们为实验室新建了个GitHub,感觉好像又好起来了,拿一枚蛋黄派作为加入我们的新手礼包

听学长说完实验室的来历之后又莫名自豪

不知道为什么

然后晚上去打羽毛球结果风太大了打不了羽毛球

于是就去小操场和大家一起玩三国杀了

期间我经历了模电作业提交风波

最后还是没写直接把荷叶饭的概率论作业交上去了

还挺燃的

然后不想回宿舍就在小操场躺下了 

然后我们超过了回宿舍时限(虽然可能是故意的只是想给自己一个必须出去的理由)

然后就开始润去KTV了没错又是无用知识点(晚上订KTV要提前预定,否则你会十二点之后过去发现人家满包了)但是我们还是比较幸运的,在辗转了三个KTV之后终于找到了最后一个包间,我们无痛小包升大包,于是开始在实验室拉人(12:50)

但是没有人鸟我们都睡着了

哈哈,我的车又被推上去了

昨天起床下楼还碰到了和我一样没梳头的荷叶饭,,,真是令人忍俊不禁啊 


http://www.mrgr.cn/news/38370.html

相关文章:

  • MATLAB中的并行计算:提升性能的策略与实践
  • 双向链表的实现
  • 为什么说AI普及之路还存在挑战呢?
  • 参数高效的迁移学习在自然语言处理中的应用
  • mov视频怎么转换成mp4?这几种转换方法值得收藏起来!
  • C++里的随机数
  • CentOS 修改服务器登录密码的完整指南
  • 【C语言】动态内存管理:malloc、calloc、realloc、free
  • vue3使用Teleport 控制台报警告:Invalid Teleport target on mount: null (object)
  • c++进阶学习--------多态
  • Java如何在方法中操作数组元素
  • 计算机网络各层有哪些协议?计算机网络协议解析:从拟定到实现,全面了解各层协议的作用与区别
  • 业务资源管理模式语言19
  • 05-成神之路_ambari_Ambari实战-013-代码生命周期-metainfo-configFiles详解
  • 关系型数据库的特点
  • 工控主板在工业控制中扮演什么角色
  • k8s基于nfs创建storageClass
  • 【2023工业3D异常检测文献】PointCore: 基于局部-全局特征的高效无监督点云异常检测器
  • 2024年7月大众点评温州美食店铺基础信息
  • 综合业务区的数字化创新与智能化蓝图