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

【Linux】生产者消费者模型:基于阻塞队列,使用互斥锁和条件变量维护互斥与同步关系

目录

一、什么是生产者消费者模型

二、为什么要引入生产者消费者模型?

三、详解生产者消费者模型 ​编辑

生产者和生产者、消费者和消费者、生产者和消费者,它们之间为什么会存在互斥关系?

生产者和消费者之间为什么会存在同步关系?

为什么生产者与消费者之间需要一个交易场所?

四、基于阻塞队列的生产者消费者模型

生产者消费者模型的使用场景示例:

五、理解生产者消费者模型代码


一、什么是生产者消费者模型

生产者-消费者模型是一种常见的并发设计模式,用于处理在生产和消费任务之间的协调。这个模型主要用来解决在多线程或多进程环境中,生产者和消费者之间的同步和数据共享问题。

在这个模型中:

  • 生产者:负责生成数据或任务,并将其放入一个共享的缓冲区(也称为队列)中。
  • 消费者:从共享缓冲区中取出数据或任务并进行处理。

缓冲区的作用是实现生产者和消费者之间的解耦,使得生产者和消费者可以在不同的速度下独立工作。

二、为什么要引入生产者消费者模型?

在没有这种模型的情况下,生产者和消费者可能会遇到以下问题:

  • 资源浪费:如果生产者生产速度过快,而消费者消费速度跟不上,可能会导致生产出的产品(通常是数据或任务)被丢弃,造成资源浪费。
  • 资源不足:如果消费者消费速度过快,而生产者生产速度跟不上,消费者可能会因为等待新资源而处于空闲状态,造成资源不足。
  • 竞态条件:在没有适当的同步机制的情况下,多个生产者或消费者同时访问共享资源(如队列)可能会导致数据不一致或状态错误。
  •  死锁:如果生产者和消费者在等待对方释放资源时都阻塞了,可能会导致死锁,即系统无法继续前进。
  • 饥饿:某些消费者可能因为其他消费者不断地获取资源而长时间得不到服务,这种现象称为饥饿。

生产者-消费者模型通过引入缓冲区(如队列)和同步机制(如信号量、互斥锁)来解决这些问题。模型通常包含以下几个关键组件:

  • 生产者:负责生成数据或任务。
  • 消费者:负责处理数据或任务。
  • 缓冲区:存储生产者生产的数据,供消费者使用。
  • 同步机制:确保生产者和消费者之间的协调,避免竞态条件和死锁。

通过这些组件,生产者-消费者模型可以有效地管理资源,确保生产者不会在缓冲区满时生产,消费者不会在缓冲区空时消费,从而实现生产者和消费者之间的平衡和同步。

三、详解生产者消费者模型 

生产者消费者模型是多线程同步与互斥的一个经典场景,其特点如下:

  • 三种关系: 生产者和生产者(互斥关系)、消费者和消费者(互斥关系)、生产者和消费者(互斥关系、同步关系)。
  • 两种角色: 生产者和消费者。(通常由进程或线程承担)
  • 一个交易场所: 通常指的是内存中的一段缓冲区。(如阻塞队列、环形队列等)

我们在用代码编写生产者消费者模型的时,本质就是对这三个特点进行维护。

生产者和生产者、消费者和消费者、生产者和消费者,它们之间为什么会存在互斥关系?

生产者将生产的数据放入容器中,而消费者又会从容器中取出数据,这就造成了在同一时刻中,容器中的数据可能被多个执行流访问。而该容器其实就是临界资源,对于临界资源,我们必须保护对临界资源操作的原子性,否则容易造成数据错乱。

因此我们必须保证生产者和消费者访问临界资源的操作是串行的。每次访问,我们只允许一个执行流进入临界区。如此,我们就保证了临界资源的安全。

所以,在访问临界区资源之前,无论是生产者还是消费者,必须先去竞争保护临界资源的那把互斥锁。即生产者和消费者会进行同一把锁的竞争!

生产者和消费者之间为什么会存在同步关系?

如果让生产者一直生产,那么当生产者生产的数据将容器塞满后,生产者再生产数据就会生产失败。反之,让消费者一直消费,那么当容器当中的数据被消费完后,消费者再进行消费就会消费失败。

虽然这样不会造成任何数据不一致的问题,但是这样会引起另一方的饥饿问题,是非常低效的。我们应该让生产者和消费者访问该容器时具有一定的顺序性,比如让生产者先生产,然后再让消费者进行消费。

当缓冲区为满时,生产者需要在自己的条件变量下阻塞等待,直到有消费者进行消费,缓冲区有空间剩余时,才会继续与消费者竞争临界资源的管理权。

当缓冲区为空时,消费者需要在自己的条件变量下阻塞等待,直到有生产者进行生产,缓冲区有数据时,才会继续与生产者竞争临界资源的管理权。

【注意: 互斥关系保证的是数据的正确性,而同步关系是为了让多线程之间协同起来。】

为什么生产者与消费者之间需要一个交易场所?

生产者与消费者之间需要一个交易场所,通常称为缓冲区(Buffer),是因为这个缓冲区在多线程环境中起到了至关重要的作用。以下是缓冲区的几个关键作用:

  1. 协调生产和消费:缓冲区作为生产者和消费者之间的中介,允许生产者在缓冲区有空间时生产数据,消费者在缓冲区有数据时消费数据。这种协调机制避免了生产者和消费者之间的直接竞争,确保了生产和消费的有序进行。

  2. 解耦生产者和消费者:缓冲区使得生产者和消费者不必同时运行。生产者可以在没有消费者等待的情况下生产数据,消费者也可以在没有生产者生产的情况下消费数据。这种解耦提高了系统的灵活性和效率。

  3. 防止数据丢失:在没有缓冲区的情况下,如果生产者生产速度过快,消费者可能无法及时消费,导致数据丢失。缓冲区提供了一个存储空间,可以暂时保存生产者生产的数据,直到消费者准备好消费。

  4. 提高并行性:缓冲区允许生产者和消费者以不同的速度独立工作,提高了系统的并行性和吞吐量。生产者可以快速生产数据,而消费者可以根据自己的速度消费数据,两者互不干扰。

  5. 避免死锁和饥饿:通过适当的同步机制(如信号量和条件变量),缓冲区可以避免死锁和饥饿问题。生产者在缓冲区满时等待,消费者在缓冲区空时等待,这些等待条件可以通过同步机制得到管理,确保所有线程都能在适当的时候获得资源。

  6. 实现负载均衡:缓冲区可以平滑生产者和消费者之间的负载波动。在生产者负载高时,缓冲区可以存储额外的数据,而在消费者负载高时,缓冲区可以提供足够的数据供消费。

  7. 简化线程管理:缓冲区提供了一个明确的接口,使得线程管理更加简单。生产者和消费者只需要关注缓冲区的状态,而不需要直接管理其他线程。

总之,缓冲区作为生产者与消费者之间的交易场所,是实现高效、稳定和可扩展的多线程系统的关键组件。它通过协调生产和消费、解耦生产者和消费者、防止数据丢失、提高并行性、避免死锁和饥饿以及实现负载均衡等方式,提高了系统的整体性能和可靠性。

四、基于阻塞队列的生产者消费者模型

什么是阻塞队列?它的作用是什么?

在多线程编程中,阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。

生产者将数据放入队列,消费者从队列中取出数据。

  • 当队列为空时,消费者会阻塞等待,直到有数据可用;
  • 当队列满时,生产者会阻塞等待,直到有空间可用。 

通过阻塞操作,阻塞队列可以有效地管理资源,避免过度生产或消费,从而提高并发性能。

#pragma once
#include <pthread.h>
#include <iostream>
#include <queue>
#include "Thread.hpp"static const int MAX_CAPACITY = 6;template <typename T>
class BlockQueue
{
private:std::queue<T> _block_queue;//阻塞队列,临界资源int _max_capacity;//队列最大容量pthread_mutex_t _mutex;//生产者和消费者之间需要满足互斥关系。因为当生产者在生产时,消费者不能去消费,否则容易造成临界资源错乱。反之亦然pthread_cond_t _producer_cond;//当生产资源达到最大容量的时候,生产者需要在生产者的条件变量下等待,当有空余空间时,在进行生产,由消费者告知生产者来生产pthread_cond_t _consumer_cond;//当生产资源被消费完之后,消费者需要在消费者条件变量下等待,直到新的资源被生产,由生产者唤醒消费者来进行消费//在生产资源未到最大容量和生产资源未空时,消费者和生产者形成互斥关系,竞争临界资源的管理权
public://构造BlockQueue(int capacity = MAX_CAPACITY):_max_capacity(capacity){pthread_mutex_init(&_mutex, nullptr);//初始化锁pthread_cond_init(&_producer_cond, nullptr);//初始化条件变量pthread_cond_init(&_consumer_cond, nullptr);}void Push(const T& in){//生产者和消费者竞争同一把锁pthread_mutex_lock(&_mutex);while(IsFull())//为什么用while,不用if? 防止当有多个生产者时,使用broadcast会造成多个线程被唤醒,而此时多个线程已经经过了if判断。//使用if只能判断一次,无法避免时间差所带来的条件改变,因此需要循环检查。{//生产者在生产者的条件变量处等待,等待时释放锁。被唤醒时重新参与锁的竞争pthread_cond_wait(&_producer_cond, &_mutex);}//走到这里一定不为满_block_queue.push(in);pthread_mutex_unlock(&_mutex);//走到这个地方说明此时阻塞队列中一定有数据,可以唤醒消费者线程pthread_cond_signal(&_consumer_cond);}void Pop(T* out)    //输出型参数,将数据带出来{//生产者和消费者竞争同一把锁pthread_mutex_lock(&_mutex);while(IsEmpty()){//消费者在消费者的条件变量处等待,等待时释放锁,被唤醒时重新参与锁的竞争。竞争到锁之后才能返回pthread_cond_wait(&_consumer_cond, &_mutex);}//走到这里一定不为空*out = _block_queue.front();_block_queue.pop();pthread_mutex_unlock(&_mutex);//走到这个地方说明阻塞队列中一定有剩余空间,可以唤醒生产者pthread_cond_signal(&_producer_cond);}bool IsFull(){return _block_queue.size() == _max_capacity;}bool IsEmpty(){return _block_queue.empty();}//析构~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_producer_cond);pthread_cond_destroy(&_consumer_cond);}
};

需要注意的是,条件变量的等待必须要放在while循环中进行条件判断。

  • 在多线程环境中,多个线程可能同时操作共享资源。即使一个线程被唤醒,也可能因为其他线程的操作使得条件不再满足。因此,在条件变量的等待过程中,必须持续检查条件是否真的符合期望,确保线程在安全的条件下继续执行。
  • 在pthread_cond_wait函数的等待队列中可能存在多个等待线程。如果使用 if 进行条件判断,仅能在该执行流执行管理资源的代码前进行判断。假如此时阻塞队列中恰好生产了一个数据,而条件变量的等待队列中恰好有多个线程被其他线程使用pthread_cond_broadcast函数同时唤醒,无论

为什么不使用 if 判断呢?

  • pthread_cond_wait函数是让当前执行流进行等待的函数,是函数就意味着有可能调用失败,调用失败后该执行流就会继续往后执行。
  • 其次,在多消费者的情况下,当生产者生产了一个数据后如果使用pthread_cond_broadcast函数唤醒消费者,此时在pthread_cond_wait函数的等待队列中可能存在多个等待线程,因此就会一次性唤醒多个消费者,但待消费的数据只有一个,此时其他消费者就被伪唤醒了。
  • 为了避免出现上述情况,我们就要让线程被唤醒后再次进行判断,确认是否真的满足生产消费条件,因此这里必须要用while进行判断。

生产者消费者模型的使用场景示例:

生产者承担的工作:不断地将创建的任务放入阻塞队列中。

消费者承担的工作:不断地从阻塞队列中提取任务,并进行执行。

阻塞队列要让生产者线程向队列中Push数据,让消费者线程从队列中Pop数据,因此这个阻塞队列必须要让这两个线程同时看到,所以我们在创建生产者线程和消费者线程时,需要将该阻塞队列作为线程执行例程的参数进行传入。

#include "Blocking_Queue.hpp"
#include <functional>
#include <unistd.h>
#include <ctime>
using task_t = std::function<void()>;void download()
{std::cout << "DownLooad ......" << std::endl;
}void *Productor(void *block_queue)
{BlockQueue<task_t> *bq = static_cast<BlockQueue<task_t> *>(block_queue);while (true){std::cout << "Producing ......" << std::endl;bq->Push(download);sleep(1);}return (void*)0;
}void *Consumer(void *block_queue)
{BlockQueue<task_t> *bq = static_cast<BlockQueue<task_t> *>(block_queue);while (true){sleep(1);std::cout << "Consuming ......" << std::endl;task_t out;bq->Pop(&out);out();}return (void*)0;
}int main()
{BlockQueue<task_t> block_queue;pthread_t productor_tid, consumer_tid;pthread_create(&productor_tid, nullptr, Productor, static_cast<void *>(&block_queue));pthread_create(&consumer_tid, nullptr, Consumer, static_cast<void *>(&block_queue));pthread_join(productor_tid, nullptr);pthread_join(consumer_tid, nullptr);return 0;
}

由于代码中生产者是每隔一秒生产一个数据,而消费者是每隔一秒消费一个数据,因此运行代码后我们可以看到生产者和消费者的执行步调是一致的。

需要注意的是:由于我们将BlockingQueue当中存储的数据进行了模板化,此时就可以让BlockingQueue当中存储其他类型的数据。这点可根据需求自行修改。

五、理解生产者消费者模型代码

看完上述内容后,同学们可能会有这种疑惑:生产者消费者模型不是为了实现高并发而设计的吗?为什么在生产者创建任务时和消费者提取任务时是串行的,而不是并行的?

在回答这个问题之前,我们需要了解一下什么是并发,什么又是并行。

并发(Concurrency):

定义:并发指的是系统能够处理多个任务的能力,这些任务可能是同时进行的,也可能是交替进行的。在并发的场景下,任务可以在同一时间段内交替执行,但不一定是同时的并发更关注的是任务的切换和管理。

并行(Parallelism)

定义:并行指的是系统能够真正地同时执行多个任务。在并行的场景下,多个任务在同一时间段内在不同的处理器或计算核心上同时执行。并行更多地关注的是计算任务的真正同时处理。

在生产者消费者模型中,生产者和消费者对阻塞队列的操作仅仅是向阻塞队列中添加和提取数据,而阻塞队列是共享资源,我们必须对其进行线程安全的保护,让各个执行流串行地去执行临界区代码,保证对临界资源操作的原子性。

添加与提取任务恰恰是生产者消费者模型中执行比较快速的操作。我们可以假设此时有多个生产者线程,此时只能有一个生产者线程能够进入临界区中。那么,在同一时刻,其他的生产者线程一定都在等待进入临界区中呢?答案是否定的!

我们需要知道,今天我们只是使用该模型执行了简单的任务。当遇见复杂任务时生产者需要时间来构建任务!反之,消费者也需要时间来执行任务! 在某个生产者线程向阻塞队列中添加任务时,其他生产者线程可能在等待,也可能在进行任务的生产。消费者线程亦然!

由此我们可以看出,上述代码实现的生产者消费者模型并不是低效的。相反,它有效地管理共享资源的访问、合理安排线程的任务,并确保系统在处理复杂任务时仍能保持高效和稳定。同时解决了生产者与消费者可能出现的忙闲不均的问题,实现了负载均衡。

相关文章:

【Linux】生产者消费者模型:基于阻塞队列,使用互斥锁和条件变量维护互斥与同步关系

目录 一、什么是生产者消费者模型 二、为什么要引入生产者消费者模型&#xff1f; 三、详解生产者消费者模型 ​编辑 生产者和生产者、消费者和消费者、生产者和消费者&#xff0c;它们之间为什么会存在互斥关系&#xff1f; 生产者和消费者之间为什么会存在同步关系&…...

多层感知机paddle

多层感知机——paddle部分 本文部分为paddle框架以及部分理论分析&#xff0c;torch框架对应代码可见多层感知机 import paddle print("paddle version:",paddle.__version__)paddle version: 2.6.1多层感知机&#xff08;MLP&#xff0c;也称为神经网络&#xff0…...

linux-网络管理-网络服务管理 17 / 100

Linux 网络管理&#xff1a;网络服务管理 一、概述 在 Linux 系统中&#xff0c;网络服务管理是系统管理中的重要组成部分。网络服务通常涉及到多种协议、服务和工具&#xff0c;用于确保服务器与客户端、局域网与广域网、以及不同系统之间的通信畅通。Linux 提供了强大的工具…...

Docker上安装mysql

获取 MySQL 镜像 获取镜像。使用以下命令来拉取镜像&#xff1a; 1docker pull mysql:latest 这里拉取的是最新版本的 MySQL 镜像。你也可以指定特定版本&#xff0c;例如&#xff1a; 1docker pull mysql:8.0 运行 MySQL 容器 运行 MySQL 容器时&#xff0c;你需要指定一些…...

【秋招笔试-支持在线评测】8.28华为秋招(已改编)-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 华为专栏传送🚪 -> 🧷华为春秋招笔试 目前今年秋招的笔…...

基于python上门维修预约服务数据分析系统

目录 技术栈和环境说明解决的思路具体实现截图python语言框架介绍技术路线性能/安全/负载方面可行性分析论证python-flask核心代码部分展示python-django核心代码部分展示操作可行性详细视频演示源码获取 技术栈和环境说明 结合用户的使用需求&#xff0c;本系统采用运用较为广…...

React基础教程(10):React Hooks

9.1 使用hooks理由 高阶组件为了复用,导致代码层级复杂。生命周期的复杂。写成函数组件,无状态组件,因为需要状态,又写成了class,成本高9.2 useState(保存组件状态) const [state, setState] = useState(initialState);案例:点击按钮修改name...

JVM 调优篇9 调优案例6- cpu使用过载解决办法【超赞】

一 cpu过载说明 1.1 背景说明 如果线程死锁,那么线程一直在占用CPU,这样就会导致CPU一直处于一个比较高的占用率。 1.2 代码 模拟一个死锁的代码 public class JstackDeadLockDemo {/*** 必须有两个可以被加锁的对象才能产生死锁,只有一个不会产生死锁问题*/private f…...

Spring8-事务

目录 JdbcTemplate 声明式事务 事务 概述 特性&#xff08;ACID&#xff09; 编程式事务 声明式事务 基于注解的声明式事务 Transactional注解标识的位置 事务属性&#xff1a;只读 事务属性&#xff1a;超时 事务属性&#xff1a;隔离级别 事务属性&#xff1a;传…...

在Python中,类是用于定义对象的蓝图或模板,而对象则是根据类创建的具体实例

当然&#xff0c;我可以为您演示类与对象的基本概念和它们之间的关系。在Python中&#xff0c;类是用于定义对象的蓝图或模板&#xff0c;而对象则是根据类创建的具体实例。 下面是一个简单的Python程序&#xff0c;它定义了一个Car类&#xff0c;该类具有一些属性和方法&…...

【小波去噪】【matlab】基于小波分析的一维信号滤波(对照组:中值滤波、均值滤波、高斯滤波)

链接1-傅里叶变换 链接2-傅立叶分析和小波分析间的关系 链接3-小波变换&#xff08;wavelet transform&#xff09;的通俗解释 链接4-小波基的选择 1.示例代码 function main_wavelet clc clear close all warning off %% 1.信号生成 time_length 10;%总时长&#xff0c;秒 …...

CentOS 7官方源停服,配置本机光盘yum源

1、挂载系统光盘 mkdir /mnt/iso mount -o loop /tools/CentOS-7-x86_64-DVD-1810.iso /mnt/iso cd /mnt/iso/Packages/ rpm -ivh /mnt/iso/Packages/yum-utils-1.1.31-50.el7.noarch.rpm(图形界面安装&#xff0c;默契已安装&#xff09; 如安装yum-utils依赖错误&#x…...

2024年汉字小达人区级自由报名备考冲刺:2024官方模拟题练一练(续)

2024年第十一届汉字小达人的区级活动的时间9月25-30日正式开赛&#xff0c;满打满算还有9天时间。 今天继续回答一些问题关于汉字小达人的常见问题&#xff0c;再做几道2024年官方模拟题&#xff0c;帮助大家直观地了解汉字小达人的比赛题型和那你程度。 本专题在比赛前持续更…...

实战Redis与MySQL双写一致性的缓存模式

​Redis和MySQL都是常用的数据存储系统&#xff0c;它们各自有自己的优缺点。在实际应用中&#xff0c;我们可能需要将它们结合起来使用&#xff0c;比如将Redis作为缓存&#xff0c;MySQL作为持久化存储。 在这种情况下&#xff0c;我们需要保证Redis和MySQL的数据一致性&…...

KVM环境下制作ubuntu qcow2格式镜像

如果是Ubuntu KVM环境是VMware虚拟机&#xff0c;需要CPU开启虚拟化 1、配置镜像源 wget -O /etc/apt/sources.list https://www.qingtongqing.cc/ubuntu/sources.list2、安装kvm qemu-img libvirt kvm虚拟化所需环境组件 apt -y install qemu-kvm virt-manager libvirt-da…...

基于SpringBoot+Vue的高校竞赛管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…...

PHP发邮件教程:配置SMTP服务器发送邮件?

PHP发邮件的几种方式&#xff1f;如何使用PHP通过SMTP协议发信&#xff1f; PHP作为一种广泛使用的服务器端脚本语言&#xff0c;提供了多种方式来发送邮件。AokSend将详细介绍如何通过配置SMTP服务器来实现PHP发邮件教程的核心内容。 PHP发邮件教程&#xff1a;设置参数 这…...

SpringBootWeb增删改查入门案例

前言 为了快速入门一个SpringBootWeb项目&#xff0c;这里就将基础的增删改查的案例进行总结&#xff0c;作为对SpringBootMybatis的基础用法的一个巩固。 准备工作 需求说明 对员工表进行增删改查操作环境搭建 准备数据表 -- 员工管理(带约束) create table emp (id int …...

pytorch实现RNN网络

目录 1.导包 2. 加载本地文本数据 3.构建循环神经网络层 4.初始化隐藏状态state 5.创建随机的数据&#xff0c;检测一下代码是否能正常运行 6. 构建一个完整的循环神经网络 7.模型训练 8.个人知识点理解 1.导包 import torch from torch import nn from torch.nn imp…...

智能工厂的软件设计 “程序program”表达式,即 接口模型的代理模式表达式

Q1、前面将“智能工厂的软件设计”中绝无仅有的“程序”视为 专注于 给定的某个单一面&#xff08;语言面/逻辑面/数学面&#xff09;中的 问题&#xff0c;专注于分析问题和解决问题的程序活动的组织&#xff0c;每一面都是一个“组织者”就像一个“独角兽”&#xff0c;并提出…...

别再乱设target_frame了!深度解读ROS2 pointcloud_to_laserscan源码,搞懂tf转换与消息过滤器的正确用法

别再乱设target_frame了&#xff01;深度解读ROS2 pointcloud_to_laserscan源码&#xff0c;搞懂tf转换与消息过滤器的正确用法 在机器人感知系统中&#xff0c;将三维点云数据转换为二维激光扫描数据是常见的降维处理手段。ROS2的pointcloud_to_laserscan功能包看似简单&…...

保姆级教程:小米AX3000T刷OpenWrt 24.10.0全流程(含救砖指南)

小米AX3000T路由器刷OpenWrt全流程实战指南 作为一名长期折腾家用路由器的技术爱好者&#xff0c;我最近刚完成了小米AX3000T刷OpenWrt的全过程。相比官方固件&#xff0c;OpenWrt提供了更强大的自定义功能和性能优化空间。本文将分享从准备工作到救砖方案的完整经验&#xff…...

保姆级教程:用Davinci Configurator配置RH850F1KMS1双看门狗(AWO域与ISO域)

RH850F1KMS1双看门狗配置实战&#xff1a;从AWO域到ISO域的完整设计指南 在汽车电子开发领域&#xff0c;系统可靠性直接关系到行车安全。RH850F1KMS1作为瑞萨电子面向功能安全应用的高性能MCU&#xff0c;其独特的双看门狗架构&#xff08;AWO域与ISO域&#xff09;为系统提供…...

Cowabunga Lite:iOS系统个性化定制的免越狱解决方案

Cowabunga Lite&#xff1a;iOS系统个性化定制的免越狱解决方案 【免费下载链接】CowabungaLite iOS 15 Customization Toolbox 项目地址: https://gitcode.com/gh_mirrors/co/CowabungaLite 在iOS生态系统中&#xff0c;用户对系统个性化的需求与日俱增&#xff0c;但传…...

Django CORS Headers 终极指南:10个企业级跨域架构设计技巧

Django CORS Headers 终极指南&#xff1a;10个企业级跨域架构设计技巧 【免费下载链接】django-cors-headers Django app for handling the server headers required for Cross-Origin Resource Sharing (CORS) 项目地址: https://gitcode.com/gh_mirrors/dj/django-cors-he…...

当I2C总线卡死时我们在debug什么:从复位异常到多设备冲突的故障树分析

当I2C总线卡死时我们在debug什么&#xff1a;从复位异常到多设备冲突的故障树分析 I2C总线作为嵌入式系统中广泛使用的通信协议&#xff0c;其简洁的两线制设计&#xff08;SCL时钟线与SDA数据线&#xff09;背后隐藏着复杂的硬件交互逻辑。当系统突然出现I2C通信失败、设备无响…...

告别电台收听难题:foobox-cn网络电台收听方案

告别电台收听难题&#xff1a;foobox-cn网络电台收听方案 【免费下载链接】foobox-cn DUI 配置 for foobar2000 项目地址: https://gitcode.com/GitHub_Trending/fo/foobox-cn foobox-cn作为foobar2000的DUI皮肤&#xff08;桌面用户界面定制方案&#xff09;&#xff0…...

Cursor省钱神器:interactive-feedback-mcp安装配置全攻略(附常见问题排查)

Cursor省钱神器&#xff1a;interactive-feedback-mcp安装配置全攻略&#xff08;附常见问题排查&#xff09; 在AI辅助编程领域&#xff0c;Cursor凭借其强大的代码生成和智能补全功能&#xff0c;已成为开发者日常工作的得力助手。然而&#xff0c;许多用户在使用过程中常常…...

AI写的论文如何降到20%以内?分场景教程+工具对比

AI写的论文如何降到20%以内&#xff1f;分场景教程工具对比 “我用DeepSeek写了大半篇论文&#xff0c;导师要求知网AI率必须低于20%&#xff0c;现在已经是52%&#xff0c;我该怎么办&#xff1f;” 这是毕业季最典型的求助问题之一。 不同的情况&#xff0c;处理方法不一样。…...

告别Vue组件匿名时代:用vite-plugin-vue-setup-extend给你的<script setup>加个名字

为Vue组件正名&#xff1a;vite-plugin-vue-setup-extend深度整合指南 在Vue 3的组合式API开发中&#xff0c;<script setup>语法糖以其简洁性赢得了开发者的青睐。但当你打开Vue DevTools准备调试时&#xff0c;满屏的"Anonymous Component"是否曾让你感到困扰…...