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

【Linux系统编程】第四十六弹---线程同步与生产消费模型深度解析

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】

目录

1、Linux线程同步

1.1、同步概念与竞态条件 

1.2、条件变量

1.2.1、认识条件变量接口

1.2.2、举例子认识条件变量

1.2.3、测试代码 

2、生产消费模型 

2.1、为何要使用生产消费模型

2.2、生产者消费者模型优点

2.3、编写生产消费模型

2.3.1、BlockQueue类基本结构

2.3.2、构造析构函数

2.3.3、判空判满函数

2.3.4、生产者入队

2.3.5、消费者出队

2.4、测试生产消费模型

2.4.1、内置类型

2.4.2、类类型

2.4.3、函数类型

2.4.4、多生产多消费 


1、Linux线程同步

在上一弹的上锁抢票代码中我们可以看到,会有很长一段时间使用的是同一个线程,这样的方式没有错,但是不合理,怎么解决这个问题呢?

 先通过一个实际情况分析此问题,再解决该问题。

假设学校有一个VIP自习室,一次只允许一个人进来,进入自习室需要用到门口的一把锁。

  • 有一个uu今天想去里面自习,就早早5点起床去了VIP自习室,但是他又想,竟然来了就多学习一会,此时外面也有人想进来自习,但是没有钥匙只能在外面等
  • 此时这个uu已经学了一上午了,很饿了,想去吃饭,走到门口,刚放回钥匙,又后悔了,如果现在还钥匙了,后面就不能进自习室了,因此这个uu又拿了钥匙进入了自习室(因为uu离钥匙比较近,因此还是他先拿到钥匙)

结论:其他人长时间无法进入自习室 --- 无法获取临界资源 -- 导致饥饿问题!!! 

因此我们可以修改规则,让进入自习室更公平!

每一个同学归还钥匙后:

1、不能立马申请

2、第二次申请,必须排队(换句话说,其他人也得排队)

1.1、同步概念与竞态条件 

同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步
竞态条件因为时序问题,而导致程序异常,我们称之为竞态条件。在线程场景下,这种问题也不难理解

1.2、条件变量

  • 当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了
  • 例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。

1.2.1、认识条件变量接口

初始化

int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 全局或者静态只需初始化参数:cond:要初始化的条件变量attr:NULL

销毁

int pthread_cond_destroy(pthread_cond_t *cond)

等待条件满足

int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);参数:cond:要在这个条件变量上等待mutex:互斥量

唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond); // 唤醒所以线程
int pthread_cond_signal(pthread_cond_t *cond); // 唤醒一个线程

1.2.2、举例子认识条件变量

假设有两个人,一个盘子,一个人放苹果到盘子里,另一个人从盘子里取苹果(前提是有苹果,因此需要先检查是否有苹果),但是互相都不知道什么时候放和取苹果,因此只能一次次的去尝试,是够放好,是否被取,但是这样会导致一个问题,如果一个人不放,那么另一个会一直去检查盘子里有没有苹果,这样就太浪费(线程)资源了,我们可以改进一下策略!!!

优化

我们可以再加一个铃铛,当取苹果的时候,如果盘子里面还没有苹果,那么就可以在铃铛处等待,等另一个人放了苹果了,就来铃铛处通知,这样两个人就能高效利用资源了!!

铃铛就是我们讲解的条件变量:

1.需要一个线程队列

2.需要有通知机制

  • 全部叫醒
  • 叫醒一个 

1.2.3、测试代码 

新线程等待函数

const int num = 5;
pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t gcond = PTHREAD_COND_INITIALIZER;void* Wait(void* args)
{std::string name = static_cast<const char*>(args);while(true){pthread_mutex_lock(&gmutex);pthread_cond_wait(&gcond,&gmutex);usleep(10000);std::cout << "I am " << name << std::endl;pthread_mutex_unlock(&gmutex);}
}

主函数

int main()
{// 1.创建保存线程tid的数组pthread_t threads[num];for(int i=0;i<num;i++){char* name = new char[1024];snprintf(name,1024,"thread-%d",i + 1);pthread_create(threads + i,nullptr,Wait,(void*)name);usleep(1000);}sleep(1);// 2.唤醒其他线程while(true){// pthread_cond_signal(&gcond); // 唤醒一个线程pthread_cond_broadcast(&gcond); // 唤醒所有线程std::cout << "唤醒一个线程..." << std::endl;sleep(2);}// 3.终止线程for(int i=0;i<num;i++){pthread_join(threads[i],nullptr);}return 0;
}

运行结果 

2、生产消费模型 

2.1、为何要使用生产消费模型

  • 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯。
  • 所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区(一段内存空间),平衡了生产者和消费者的处理能力。
  • 这个阻塞队列就是用来给生产者和消费者解耦的

2.2、生产者消费者模型优点

  • 解耦
  • 支持并发
  • 支持忙闲不均

思考切入点:"321"原则

  • 1、一个交易场所(特定数据结构形式存在的一段内存空间)
  • 2、两种角色(生产角色 消费角色)生产线程,消费线程
  • 3、三种关系(生产和生产[互斥] 消费和消费[互斥] 生产和消费[同步和互斥])

实现生产消费模型,本质就是通过代码实现321原则,用锁和条件变量(或者其他方式)来实现三种关系!!!

2.3、编写生产消费模型

BlockingQueue

  • 在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队列进程操作时会被阻塞)

2.3.1、BlockQueue类基本结构

 此处的类设计成模板形式,让结构更加灵活!!!

template<typename T>
class BlockQueue
{
private:bool IsFull();bool IsEmpty();
public:BlockQueue(int cap = defaultcap);// 消费者出队列void Pop(T* out);// 生产者入队列void Equeue(const T& in);~BlockQueue();
private:std::queue<T> _block_queue; // 临界资源int _max_cap;pthread_mutex_t _mutex;pthread_cond_t _p_cond; // 生产着条件变量pthread_cond_t _c_cond; // 消费者条件变量
};

2.3.2、构造析构函数

构造函数用于初始化最大容量和初始化锁以及条件变量,析构函数用于释放锁和条件变量!

// 构造
BlockQueue(int cap = defaultcap) :_max_cap(cap)
{pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_p_cond,nullptr);pthread_cond_init(&_c_cond,nullptr);
}// 析构
~BlockQueue()
{pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_p_cond);pthread_cond_destroy(&_c_cond);
}

2.3.3、判空判满函数

判断是否为空即判断队列是否为空即可,判断是否未满即判断队列成员个数是否与最大容量相等!!

// 判满
bool IsFull()
{return _block_queue.size() == _max_cap;
}
// 判空
bool IsEmpty()
{return _block_queue.empty();
}

2.3.4、生产者入队

入队是将数据插入到队尾中,可能出现数据不一致问题,因此需要加锁和条件变量,如果满了则需要等待,不为满则需要插入数据,并唤醒消费者!!!

// 生产者入队列
void Equeue(const T& in)
{pthread_mutex_lock(&_mutex); // 上锁while(IsFull()){// 满了,生产着不能生产,必须等待// 可是在临界区里面!pthread_cond_wait// 被调用的时候,除了让自己排队等待,还会自己释放传入的锁// 函数返回的时候,不就还在临界区了?// 返回时:必须参与锁的竞争,重新加上锁才能返回pthread_cond_wait(&_p_cond,&_mutex);}// 1.没有满 || 2.被唤醒了_block_queue.push(in); // 生产到阻塞队列pthread_mutex_unlock(&_mutex); // 解锁pthread_cond_signal(&_c_cond); // 唤醒消费者,解锁前解锁后均可
}

2.3.5、消费者出队

出队即删除队头数据,并获取队头的数据,为空则需要等待,不为空则可以删除队头数据,并唤醒生产者!!!

// 消费者出队列
void Pop(T* out)
{pthread_mutex_lock(&_mutex);// 为空,消费者不能消费,必须等待while(IsEmpty()){// 添加尚未满足,但是线程被异常唤醒的情况,叫做伪唤醒!pthread_cond_wait(&_c_cond,&_mutex);}// 1.没有空 || 2.被唤醒*out = _block_queue.front(); // 输出型参数_block_queue.pop();pthread_mutex_unlock(&_mutex);// 唤醒生产着生产pthread_cond_signal(&_p_cond);
}

2.4、测试生产消费模型

2.4.1、内置类型

Consumer

void* Consumer(void* args)
{BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);while(true){// 1.获取数据int t;bq->Pop(&t);// 2.处理数据std::cout << "Consumer->" << t << std::endl;}
}

Productor

void* Productor(void* args)
{srand(time(nullptr) ^ getpid());BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);while(true){// 1.构建数据/任务int x = rand() % 10 + 1; // [1,10]sleep(1); // 1秒生产一个数据// 2.生产数据bq->Equeue(x);std::cout << "Productor->" << x << std::endl;}
}

主函数

int main()
{BlockQueue<int>* bq = new BlockQueue<int>();// 单生产 单消费pthread_t c,p;// 创建线程pthread_create(&c,nullptr,Consumer,bq);pthread_create(&p,nullptr,Productor,bq);// 终止线程pthread_join(c,nullptr);pthread_join(p,nullptr);return 0;
}

 运行结果 

2.4.2、类类型

Task类

设计一个加法的Task类,内部封装仿函数,测试函数!!!

class Task
{
public:Task(){}// 带参构造Task(int x, int y) : _x(x), _y(y){}// 仿函数,直接使用()访问Excute函数void operator()(){Excute();}void Excute(){_result = _x + _y;}std::string debug(){std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=?";return msg;}std::string result(){std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=" + std::to_string(_result);return msg;}
private:int _x;int _y;int _result;
};

Consumer

void *Consumer(void *args)
{BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);while (true){// 1.获取数据Task t;bq->Pop(&t);// 2.处理数据// t.Excute();t(); // 使用仿函数std::cout << "Consumer->" << t.result() << std::endl;}
}

Productor

void *Productor(void *args)
{BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);while (true){// 1.构建数据/任务int x = rand() % 10 + 1; // [1,10]usleep(1000);            // 尽量保证随机数不同int y = rand() % 10 + 1;Task t(x,y);// 2.生产数据bq->Equeue(t);std::cout << "Productor->" << t.debug() << std::endl;sleep(1);}
}

主函数

int main()
{BlockQueue<Task> *bq = new BlockQueue<Task>();// 单生产 单消费pthread_t c, p;// 创建线程pthread_create(&c, nullptr, Consumer, bq);pthread_create(&p, nullptr, Productor, bq);// 终止线程pthread_join(c, nullptr);pthread_join(p, nullptr);return 0;
}

运行结果  

2.4.3、函数类型

函数与声明与实现

// typedef std::function<void()> task_t;
using task_t = std::function<void()>; // 包装器void Download()
{std::cout << "我是一个下载的任务" << std::endl;
}

Consumer

void *Consumer(void *args)
{BlockQueue<task_t> *bq = static_cast<BlockQueue<task_t> *>(args);while (true){// 1.获取数据task_t t;bq->Pop(&t);// 2.处理数据t(); // 使用仿函数}
}

Productor

void *Productor(void *args)
{BlockQueue<task_t> *bq = static_cast<BlockQueue<task_t> *>(args);while (true){// 1.生产数据bq->Equeue(Download);std::cout << "Productor-> Download" << std::endl;sleep(1);}
}

主函数

int main()
{BlockQueue<task_t> *bq = new BlockQueue<task_t>();// 单生产 单消费pthread_t c, p;// 创建线程pthread_create(&c, nullptr, Consumer, bq);pthread_create(&p, nullptr, Productor, bq);// 终止线程pthread_join(c, nullptr);pthread_join(p, nullptr);return 0;
}

运行结果  

2.4.4、多生产多消费 

int main()
{BlockQueue<task_t> *bq = new BlockQueue<task_t>();// 多生产 多消费pthread_t c1,c2,p1,p2,p3;// 创建线程pthread_create(&c1, nullptr, Consumer, bq);pthread_create(&c2, nullptr, Consumer, bq);pthread_create(&p1, nullptr, Productor, bq);pthread_create(&p2, nullptr, Productor, bq);pthread_create(&p3, nullptr, Productor, bq);// 终止线程pthread_join(c1, nullptr);pthread_join(c2, nullptr);pthread_join(p1, nullptr);pthread_join(p2, nullptr);pthread_join(p3, nullptr);return 0;
}

运行结果  

相关文章:

【Linux系统编程】第四十六弹---线程同步与生产消费模型深度解析

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、Linux线程同步 1.1、同步概念与竞态条件 1.2、条件变量 1.2.1、认识条件变量接口 1.2.2、举例子认识条件变量 1.2.3、…...

VoIP是什么?

IP 语音 (VoIP)&#xff08;Voice over Internet Protocol&#xff09; 是一种通过互联网拨打电话的方法。与旧的固定电话系统不同&#xff0c;互联网并非设计用于在连接的人之间实时传输音频信号。必须构建专门的技术和协议才能使之成为可能&#xff0c;这些技术和协议构成了 …...

MySQL 中的集群部署方案

文章目录 MySQL 中的集群部署方案MySQL ReplicationMySQL Group ReplicationInnoDB ClusterInnoDB ClusterSetInnoDB ReplicaSetMMMMHAGalera ClusterMySQL ClusterMySQL Fabric 总结参考 MySQL 中的集群部署方案 MySQL Replication MySQL Replication 是官方提供的主从同步方…...

《设计模式》创建型模式总结

目录 创建型模式概述 Factory Method: 唯一的类创建型模式 Abstract Factory Builder模式 Prototype模式 Singleton模式 最近在参与一个量化交易系统的项目&#xff0c;里面涉及到用java来重构部分vnpy的开源框架&#xff0c;因为是框架的搭建&#xff0c;所以会涉及到像…...

Conda安装与使用中的若干问题记录

Conda安装与使用中的若干问题记录 1.Anaconda 安装失败1.1.问题复述1.2.问题解决&#xff08;安装建议&#xff09; 2.虚拟环境pip install未安装至本虚拟环境2.1.问题复述2.2.问题解决 3.待补充 最近由于工作上的原因&#xff0c;要使用到Conda进行虚拟环境的管理&#xff0c;…...

人力资源招聘系统的革新之路:从传统到智能的转变

在全球化与数字化交织的今天&#xff0c;企业间的竞争日益激烈&#xff0c;而人才作为企业发展的核心驱动力&#xff0c;其重要性不言而喻。传统的人力资源招聘方式&#xff0c;如依赖纸质简历、人工筛选、面对面面试等&#xff0c;不仅效率低下&#xff0c;且难以精准匹配企业…...

Python网络爬虫与数据采集实战——网络协议与HTTP

目录 1. HTTP协议简介 2. 常见的请求方法 3. 状态码含义 实际应用中的HTTP协议 1. 如何在爬虫中使用HTTP协议 2. 模拟浏览器请求与爬虫反爬虫技术 3. 高级HTTP请求 实现爬虫时HTTP协议的优化与常见问题 总结 1. HTTP协议简介 HTTP的定义与作用 HTTP&#xff08;超文本…...

从零开始的c++之旅——二叉搜索树

1、二叉搜索树概念 1. ⼆叉搜索树的概念 ⼆叉搜索树⼜称⼆叉排序树&#xff0c;它或者是⼀棵空树&#xff0c;或者是具有以下性质的⼆叉树: • 若它的左⼦树不为空&#xff0c;则左⼦树上所有结点的值都⼩于等于根结点的值 • 若它的右⼦树不为空&#xff0c;则右⼦树上所有结…...

CSS回顾-基础知识详解

一、引言 在前端开发领域&#xff0c;CSS 曾是构建网页视觉效果的关键&#xff0c;与 HTML、JavaScript 一起打造精彩的网络世界。但随着组件库的大量涌现&#xff0c;我们亲手书写 CSS 样式的情况越来越少&#xff0c;CSS 基础知识也逐渐被我们遗忘。 现在&#xff0c;这种遗…...

Elasticsearch 查询时 term、match、match_phrase、match_phrase_prefix 的区别

Elasticsearch 查询时 term、match、match_phrase、match_phrase_prefix 的区别 keyword 与 text 区别term 查询match 查询match_phrase 查询match_phrase_prefix 查询写在最后 在讲述 es 查询时 term、match、match_phrase、match_phrase_prefix 的区别之前&#xff0c;先来了…...

低代码平台:跨数据库处理的重要性与实现方式

一、低代码平台概述 低代码平台作为一种创新的软件开发工具&#xff0c;为开发者带来了极大的便利。它具备可视化编程工具和大量预构建组件&#xff0c;这使得开发者无需编写大量代码就能创建应用程序&#xff0c;显著降低了软件开发的技术门槛。无论是专业开发人员还是业务人员…...

【jvm】如何破坏双亲委派机制

目录 1.说明2.重写ClassLoader的loadClass方法2.1 原理2.2 实现步骤2.3 注意事项 3.使用线程上下文类加载器3.1 原理3.2 实现步骤3.3 应用场景 4.利用SPI机制4.1 原理4.2 实现步骤4.3 应用场景 5.Tomcat等容器的自定义类加载器5.1 原理5.2 实现方式5.3 应用场景 1.说明 1.双亲委…...

ReactPress与WordPress:一场内容管理系统的较量

ReactPress Github项目地址&#xff1a;https://github.com/fecommunity/reactpress WordPress官网&#xff1a;https://wordpress.org/ ReactPress与WordPress&#xff1a;一场内容管理系统的较量 在当今数字化时代&#xff0c;内容管理系统&#xff08;CMS&#xff09;已成为…...

网络安全练习之 ctfshow_web

文章目录 VIP题目限免&#xff08;即&#xff1a;信息泄露题&#xff09;源码泄露前台JS绕过协议头信息泄露robots后台泄露phps源码泄露源码压缩包泄露版本控制泄露源码(git)版本控制泄露源码2(svn)vim临时文件泄露cookie泄露域名txt记录泄露敏感信息公布内部技术文档泄露编辑器…...

在 Service Worker 中caches.put() 和 caches.add()/caches.addAll() 方法他们之间的区别

在 Service Worker 中&#xff0c;caches.put(request, response) 和 caches.add(request)/caches.addAll(requests) 方法都用于将资源添加到缓存中&#xff0c;但它们的使用场景和目的略有不同。 caches.put(request, response)&#xff0c;一用在fetch事件当中&#xff0c;由…...

UNIAPP发布小程序调用讯飞在线语音合成+实时播报

语音合成能够将文字转化为自然流畅的人声&#xff0c;提供100发音人供您选择&#xff0c;支持多语种、多方言和中英混合&#xff0c;可灵活配置音频参数。广泛应用于新闻阅读、出行导航、智能硬件和通知播报等场景。 在当下大模型火爆的今日&#xff0c;语音交互页离不开语音合…...

跳房子(弱化版)

题目描述 跳房子&#xff0c;也叫跳飞机&#xff0c;是一种世界性的儿童游戏&#xff0c;也是中国民间传统的体育游戏之一。 跳房子的游戏规则如下&#xff1a; 在地面上确定一个起点&#xff0c;然后在起点右侧画 n 个格子&#xff0c;这些格子都在同一条直线上。每个格子内…...

ubuntu22 安装 minikube

在Ubuntu 22上安装Minikube&#xff0c;你可以按照以下步骤进行&#xff1a; 安装依赖&#xff1a; 更新系统并安装必要的依赖项&#xff1a; sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl安装Docker&#xff1a; Minikube可以使用D…...

STM32 | 超声波避障小车

超声波避障小车 一、项目背题 由于超声波测距是一种非接触检测技术&#xff0c;不受光线、被测对象颜色等的影响&#xff0c;较其它仪器更卫生&#xff0c;更耐潮湿、粉尘、高温、腐蚀气体等恶劣环境&#xff0c;具有少维护、不污染、高可靠、长寿命等特点。因此可广泛应用于…...

打造旅游卡服务新标杆:构建SOP框架与智能知识库应用

随着旅游业的蓬勃兴起&#xff0c;旅游卡产品正逐渐成为市场的焦点。为了进一步提升服务质量和客户体验&#xff0c;构建一套高效且标准化的操作流程&#xff08;SOP&#xff09;变得尤为重要。本文将深入探讨如何构建旅游卡的SOP框架&#xff0c;并介绍如何利用智能知识库技术…...

通过脚本,发起分支合并请求和打tag

#!/bin/bash # Set GitLab API URL and access token GITLAB_API_URL"http://IP/api/v4" ACCESS_TOKEN"Token秘钥" # Define repository IDs declare -A repo_ids( ["gitIP:kingmq/client.git"]"123" ["gitIP:kingmq/s…...

【视频讲解】Python深度神经网络DNNs-K-Means(K-均值)聚类方法在MNIST等数据可视化对比分析...

全文链接&#xff1a;https://tecdat.cn/?p38289 分析师&#xff1a;Cucu Sun 近年来&#xff0c;由于诸如自动编码器等深度神经网络&#xff08;DNN&#xff09;的高表示能力&#xff0c;深度聚类方法发展迅速。其核心思想是表示学习和聚类可以相互促进&#xff1a;好的表示会…...

网络安全在线网站/靶场:全面探索与实践

目录 1. CyberPatriot 简介 功能与特点 适用人群 2. Hack The Box 简介 功能与特点 适用人群 3. OverTheWire 简介 功能与特点 适用人群 4. VulnHub 简介 功能与特点 适用人群 5. PortSwigger Web Security Academy 简介 功能与特点 适用人群 6. TryHackM…...

Ceph 中Crush 算法的理解

Crush&#xff08;Controlled Replication Under Scalable Hashing&#xff09;算法是一种可扩展的、分布式的副本数据放置算法&#xff0c;广泛用于存储系统中&#xff0c;特别是Ceph分布式存储系统中。以下是对CRUSH算法的详细解释&#xff1a; 一、算法原理 CRUSH算法根据…...

D70【 python 接口自动化学习】- python 基础之数据库

day70 Python综合实践 学习日期&#xff1a;20241116 学习目标&#xff1a; MySQL 数据库 Q -- Python 综合实践 学习笔记&#xff1a; 案例需求 数据内容 DDL定义 总结 1. 使用Python实现读取写入数据库操作 ps.今天去看航展了&#xff0c;歼20简直不要太快&#xff0c;明…...

C# LINQ数据访问技术

文章目录 1.LINQ 的基本概念1.1 LINQ 的优势1.2 LINQ 数据访问的方式 2.LINQ 基本操作2.1 查询语法2.2 方法语法 3.LINQ 常用查询方法3.1 Where3.2 Select3.3 OrderBy / OrderByDescending3.4 GroupBy3.5 Join3.6 Aggregate 4.LINQ 查询示例4.1 LINQ to Objects4.2 LINQ to SQL…...

【JavaSE线程知识总结】

多线程 一.创建线程1.多线程创建方式一(Thread)2.多线程创键方式二(Runnable)3.线程创建方式三 二.线程安全问题解决办法1.使用同步代码块synchornized 2 .使用Lock解决线程安全问题 三.总结 线程就是程序内部的一条执行流程 一.创建线程 常用的方法 Thread.currentThread()…...

FreeRTOS内存管理

1. 为什么要自己实现内存管理 对于内核对象&#xff0c;可以使用时分配&#xff0c;不使用时释放C语音的库函数不适应与FreeRTOS: 实现过于复杂&#xff0c;占用空间大并非线程安全的运行不确定性&#xff1a;每次运算时间不确定内存碎片化不太编译器配置不同调试难 2. 堆栈…...

利用服务工作线程serviceWorker缓存静态文件css,html,js,图片等的方法,以及更新和删除及版本控制

Service Worker 是一种运行在浏览器背后的独立线程&#xff0c;可以用来处理推送通知、后台同步、缓存等任务。以下是使用 Service Worker 来缓存图片的一个基本示例&#xff1a; 1、注册 Service Worker: 首先&#xff0c;你需要在你的 JavaScript 文件中注册 Service Worker。…...

MuMu模拟器安卓12安装Xposed 框架

MuMu模拟器安卓12安装Xposed 框架 当开启代理后,客户端会对代理服务器证书与自身内置证书展开检测,只要检测出两者存在不一致的情况,客户端就会拒绝连接。正是这个原因,才致使我们既没有网络,又抓不到数据包。 解决方式: 通过xposed框架和trustmealready禁掉app里面校验…...