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

Linux_线程同步生产者消费者模型

同步的相关概念

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

同步的意义
互斥保证安全性,安全不一定合理或高效!!同步主要是在保证安全的前提下,让系统变得更加合理和高效!

条件变量

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

条件变量函数

pthread_cond_init函数

// 初始化局部的条件变量
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t
*restrict attr);// 使用宏值初始化(全局),类比pthread_mutex_init
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • cond 是指向条件变量对象的指针,该对象将被初始化。
  • attr 是指向条件变量属性的指针,用于指定条件变量的属性。如果此参数为 NULL,则使用默认属性。在大多数应用中,通常传递 NULL

函数成功时返回 0;出错时返回错误码。

pthread_cond_wait函数

// 使线程在条件变量上等待,直到该条件变量被另一个线程的信号
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict
mutex);
  • cond 是指向条件变量对象的指针。
  • mutex 是指向互斥锁对象的指针,该互斥锁必须在调用 pthread_cond_wait 之前被当前线程持有(即锁定状态)。

pthread_cond_signal函数

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

pthread_cond_broadcast函数 

// 唤醒所有线程
int pthread_cond_broadcast(pthread_cond_t *cond);

一个Demo样例

// 初始化全局的锁和条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void* active(void* args)
{std::string name = static_cast<const char*>(args);while(true){pthread_mutex_lock(&mutex);// 让每一个线程去条件变量那里去等待pthread_cond_wait(&cond, &mutex);    printf("%s is active!\n", name.c_str());pthread_mutex_unlock(&mutex);}
}
int main()
{pthread_t tid1, tid2, tid3;pthread_create(&tid1, nullptr, active, (void*)"thread-1");pthread_create(&tid2, nullptr, active, (void*)"thread-2");pthread_create(&tid3, nullptr, active, (void*)"thread-3");while(true){printf("main wakeup thread...\n");// 我唤醒一个线程,一个线程去做相应的工作pthread_cond_signal(&cond);sleep(1);}pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);pthread_join(tid3, nullptr);return 0;    
}

将上面的pthread_cond_signal换成下面这句代码,线程就会全部被唤醒。

pthread_cond_broadcast(&cond);

生产者消费者模型

什么是生产者消费者模型

生产者消费者模型(Producer-Consumer Model)是一种经典的多线程同步问题,用于描述两个或多个线程之间共享有限资源的场景。在这个模型中,生产者负责生成数据并将其放入缓冲区(Buffer),消费者则从缓冲区中取出数据并消费。生产者和消费者通过缓冲区进行通信,但它们的执行速度可能不同,因此需要通过同步机制来协调它们的行为。

为什么要使用生产者消费者模型

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

生产者消费者模型的优点:解耦、支持并发、支持忙闲不均

1分钟记住生产者消费者模型

生产者消费者模型可简单理解为321原则

  • 三种关系(生产和生产(互斥),消费和消费(互斥),生产和消费(互斥&&同步))
  • 两种角色(生产线程,消费线程)
  • 一个缓冲区(一段内存空间,如阻塞队列)

基于BlockingQueue的生产者消费者模型

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

Blocking Queue的Demo代码

设计思路:

(1)定义阻塞队列

阻塞队列是生产者和消费者之间的通信桥梁。它需要支持以下操作:

  • 入队(put):生产者调用,将数据放入队列。如果队列已满,则阻塞生产者线程,直到有空间可用。
  • 出队(take):消费者调用,从队列中取出数据。如果队列为空,则阻塞消费者线程,直到有数据可用。

(2)生产者逻辑

生产者负责生成数据并将其放入阻塞队列。

  1. 生成数据。

  2. 调用阻塞队列的put方法将数据放入队列。

  3. 如果队列已满,生产者线程会被阻塞,直到有空间可用。

(3)消费者逻辑

消费者负责从阻塞队列中取出数据并消费。

  1. 调用阻塞队列的take方法从队列中取出数据。

  2. 如果队列为空,消费者线程会被阻塞,直到有数据可用。

  3. 消费数据

// BlockQueue.hpp
#pragma once
#include <iostream>
#include <queue>static const int gcap = 10;
template <typename T>
class BlockQueue
{
private:bool IsFull() { return _q.size() == _cap; }bool IsEmpty() { return _q.empty(); }
public:BlockQueue(int cap = gcap): _cap(cap), _cwait_num(0), _pwait_num(0){// 基于RAII设计初始化和销毁pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_productor_cond, nullptr);pthread_cond_init(&_consumer_cond, nullptr);}void Put(const T &in) // 生产者{pthread_mutex_lock(&_mutex);// 为了防止伪唤醒,我们这里使用while进行判断// 伪唤醒是指将线程唤醒之后,线程没有资源可以访问,然后继续去等待while (IsFull()){_pwait_num++; // 若空间满了,就让等待的生产者数量++// 若满了,则生产者不能生产,必须在条件变量下等待// 临界区里面抱着锁等待是不被允许的!!!所以pthread_cond_wait// 被调用的时候:除了指明自己在那个条件变量下等待,还会释放自己抱着的锁pthread_cond_wait(&_productor_cond, &_mutex);// 返回时,线程被唤醒 && 重新申请并持有锁(它会在临界区内醒来!)// 才能继续往下运行_pwait_num--; // 生产者被唤醒,数量--}// while(IsFull())条件不满足 || 线程被唤醒, 退出循环_q.push(in);// 肯定有数据,唤醒消费者if (_cwait_num)pthread_cond_signal(&_consumer_cond);pthread_mutex_unlock(&_mutex);}void Take(T *out) // 消费者{pthread_mutex_lock(&_mutex);while (IsEmpty()){_cwait_num++; // 若数据空了,就让等待的消费者数量++pthread_cond_wait(&_consumer_cond, &_mutex);_cwait_num--; // 消费者被唤醒,数量--}*out = _q.front();_q.pop();// 肯定有空间,唤醒生产者if (_pwait_num)pthread_cond_signal(&_productor_cond);pthread_mutex_unlock(&_mutex);}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_productor_cond);pthread_cond_destroy(&_consumer_cond);}private:std::queue<T> _q;               // 使用普通队列作为保存数据的容器int _cap;                       // bq最大容量pthread_mutex_t _mutex;         // 互斥pthread_cond_t _productor_cond; // 生产者条件变量pthread_cond_t _consumer_cond;  // 消费者条件变量int _cwait_num;                 // 在条件变量下等待的消费者数量int _pwait_num;                 // 在条件变量下等待的生产者数量
};
// Main.cc
#include "BlockQueue.hpp"
#include <pthread.h>
#include <unistd.h>void* Consumer(void* args)
{BlockQueue<int> *bq = static_cast<BlockQueue<int>*>(args);while(true){sleep(2);int data;// 1.从bq中拿到数据bq->Take(&data);// 2.做各种处理printf("Consumer, 消费了一个数据: %d\n", data);}
}
void* Productor(void* args)
{BlockQueue<int> *bq = static_cast<BlockQueue<int>*>(args);// 1.从外部获取数据int data = 10;while(true){// 2.生产到bq中printf("Productor, 生产了一个数据: %d\n", data);bq->Put(data);data++;}
}
int main()
{BlockQueue<int> *bq = new BlockQueue<int>(4);// 共享资源// 单生产,单消费pthread_t c, p;// 最后一个参数传递bq,保证同一个线程看到同一个队列pthread_create(&c, nullptr, Consumer, bq);pthread_create(&p, nullptr, Productor, bq);pthread_join(c, nullptr);pthread_join(p, nullptr);delete bq;return 0;
}

让消费者睡眠2秒,生产者正常运行

让生产者睡眠2秒,消费者正常运行

ps. 上面代码是单生产单消费模式的,若想改为多生产多消费的,直接添加线程就可以。因为在生产者消费者模型的321原则中,21已经有了,3中的其中一种关系(生产和消费)已经有了,只需要补充完剩下的两种就可以。而我们设计的生产者与生产者或者消费者与消费者之间本身就只有一把锁,所以它们天然的就存在互斥关系!

基于环形队列的生产者消费者模型

认识POSIX信号量以及接口

信号量本身就是一个计数器,它用来描述你所申请资源数目的多少

信号量原理:一元信号量(或二元信号量)主要用于实现资源的互斥访问。当一个线程(或进程)获取了信号量(将其值从1减为0),它便获得了对某个共享资源的独占访问权,其他试图获取该信号量的线程将被阻塞,直到信号量被释放(其值从0加回1)。

信号量对公共资源使用时可以整体使用,也可以不整体使用。整体使用就是把整个资源看作一份。当二元信号量的值为1时,表示资源未被占用,线程可以获取信号量并进入临界区;当信号量的值为0时,表示资源已被占用,其他线程必须等待。(如上面的阻塞队列)。

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。但POSIX可以用于线程间同步

信号量的两个重要操作

1. P操作(Wait操作)

P操作(也称为sem_wait)用于等待信号量,其目的是减少信号量的值,并根据信号量的当前值决定是否阻塞当前线程或进程。

2. V操作(Post操作)

V操作(也称为sem_post)用于释放信号量,其目的是增加信号量的值,并可能唤醒等待该信号量的线程或进程。

 sem_init函数
// 初始化信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

参数

  • sem:指向要初始化的信号量对象的指针。
  • pshared:控制信号量的作用域。如果pshared的值非0,则信号量在进程间共享;如果为0,则信号量仅在调用进程内的线程间共享
  • value:信号量的初始值。这个值必须是非负的。

返回值

成功时,sem_init 返回0。失败时,返回-1,并设置errno以指示错误

sem_destroy函数
// 销毁信号量
int sem_destroy(sem_t *sem);

参数

  • sem:指向要销毁的信号量对象的指针。

返回值

  • 成功时,sem_destroy 返回 0。失败时,返回 -1,并设置 errno 以指示错误。
sem_wait函数
// 等待信号量
int sem_wait(sem_t *sem); //P()

参数

  • sem:指向要操作的信号量对象的指针。

返回值

  • 成功时返回0。失败时返回-1,并设置errno以指示错误。

该函数用于信号量的P操作,从信号量的值中减去1,如果信号量的值变为0,则当前线程将被阻塞,直到信号量的值被其他线程通过 sem_post 增加。

sem_post函数
// 发布信号量
int sem_post(sem_t *sem);//V()

参数

  • sem:指向要操作的信号量的指针。

返回值

  • 成功时,sem_post返回0。
  • 失败时,返回-1,并设置errno以指示错误。可能的错误包括EINVAL,表示传入的信号量不是一个有效的信号量。

该函数用于信号量的V操作,将信号量的值增加1;唤醒等待线程:如果有线程因为调用sem_wait函数而在该信号量上等待(即信号量的值为0且线程被阻塞),那么sem_post函数可能会唤醒其中一个等待的线程。具体唤醒哪个线程取决于操作系统的线程调度策略。

环形队列的Demo代码

这里环形队列我们使用数组模拟,用模运算来模拟环状特性。

设计思路:

1. 环形队列的结构

环形队列是一个固定大小的数组,通过两个指针(headtail)来表示队列的头部和尾部。队列的头部用于消费者读取数据,队列的尾部用于生产者写入数据。

2. 生产者和消费者线程

2.1 生产者线程

  • 使用信号量P(sem_wait函数)操作进行空间的预定,若没有空间则进行等待

  • 加锁

  • 写入数据:将数据写入 tail 指向的位置。

  • 更新 tail:将 tail 向前移动一位:tail = (tail + 1) % size

  • 解锁

  • 释放一个数据,即消费者有新数据可用

2.2 消费者线程

  • 使用信号量V(sem_post函数)操作进行数据的预定,若没有数据则进行等待

  • 加锁

  • 读取数据:从 head 指向的位置读取数据。

  • 更新 head:将 head 向前移动一位:head = (head + 1) % size

  • 解锁

  • 释放一个空间,即生产者有空闲位置可用

3. 同步机制

为了确保生产者和消费者之间的同步,需要使用互斥锁(mutex)和信号量(semaphore)来控制对共享资源的访问。

  • 互斥锁:用于保护生产者线程和消费者线程,确保同一时间只有一个线程可以修改队列。

  • 信号量:

    • sem_wait:表示等待信号量,从信号量的值中减去1

    • sem_post:表示发布信号量,从信号量的值中加1, 并唤醒等待的信号量

// Sem.hpp
#pragma once
#include <semaphore.h>int defaultvalue = 1;
class Sem
{
public:Sem(int value = defaultvalue){int n = ::sem_init(&_sem, 0, value);(void)n;}void P(){int n = ::sem_wait(&_sem);(void)n;}void V(){int n = ::sem_post(&_sem);(void)n;}~Sem(){int n = ::sem_destroy(&_sem);(void)n;}private:sem_t _sem;        // 信号量int _init_value;
};
// RingBuffer.hpp
#pragma once
#include <iostream>
#include <vector>
#include <pthread.h>
#include "Sem.hpp"template <typename T>
class RingBuffer
{
public:RingBuffer(int size): _ring(size),    // 给vector初始化一个大小_size(size),_tail(0),       // 生产者的默认位置_head(0),       // 消费者的默认位置_datasem(0),    // 默认数据为0_spacesem(size) // 默认空间为满{int n = ::pthread_mutex_init(&_p_lock, nullptr);(void)n;int m = ::pthread_mutex_init(&_c_lock, nullptr);(void)m;}// 生产者void Equeue(const T &in){_spacesem.P(); // 预定一个空间// 上锁pthread_mutex_lock(&_p_lock);_ring[_tail] = in; // 生产_tail++;_tail %= _size; // 维持环形特性pthread_mutex_unlock(&_p_lock);_datasem.V(); // 释放一个数据}// 消费者void Pop(T *out){_datasem.P(); // 预定一个数据// 上锁pthread_mutex_lock(&_c_lock);*out = _ring[_head]; // 取出数据_head++;_head %= _size;pthread_mutex_unlock(&_c_lock);_spacesem.V(); // 释放一个空间}~RingBuffer(){int n = ::pthread_mutex_destroy(&_p_lock);(void)n;int m = ::pthread_mutex_destroy(&_c_lock);(void)m;}private:std::vector<T> _ring;    // 环, 临界资源int _size;               // 总容量int _tail;               // 生产者位置int _head;               // 消费位置Sem _datasem;            // 数据信号量Sem _spacesem;           // 空间信号量pthread_mutex_t _p_lock; // 生产者锁pthread_mutex_t _c_lock; // 消费者锁
};
// Main.cc
#include "RingBuffer.hpp"
#include <string>
#include <unistd.h>// 生产
void *Consumer(void *args)
{RingBuffer<int> *ring_buffer = static_cast<RingBuffer<int> *>(args);int data = 0;while (true){// sleep(1);ring_buffer->Equeue(data);std::cout << "生产了一个数据: " << data << std::endl;data++;}return nullptr;
}
// 消费
void *Productor(void *args)
{RingBuffer<int> *ring_buffer = static_cast<RingBuffer<int> *>(args);while (true){sleep(2);int data;ring_buffer->Pop(&data);std::cout << "消费了一个数据: " << data << std::endl;}return nullptr;
}int main()
{RingBuffer<int> *ring_buffer = new RingBuffer<int>(5); // 共享资源 -> 临界资源pthread_t c1, p1, c2, c3, p2;pthread_create(&c1, nullptr, Consumer, ring_buffer);pthread_create(&c2, nullptr, Consumer, ring_buffer);pthread_create(&c3, nullptr, Consumer, ring_buffer);pthread_create(&p1, nullptr, Productor, ring_buffer);pthread_create(&p2, nullptr, Productor, ring_buffer);pthread_join(c1, nullptr);pthread_join(c2, nullptr);pthread_join(c3, nullptr);pthread_join(p1, nullptr);pthread_join(p2, nullptr);delete ring_buffer;return 0;
}

相关细节:

1.为了完成多生产和多消费的模型,消费者和生产者的同步和互斥问题我们的环形队列中已经解决。除此之外,我们还需要维护生产者和生产者 &&消费者和消费者之间的互斥关系。由于环形队列的下标也是属于临界资源的,如果不维持关系内部的互斥关系,是一定会破坏环形队列结构的。所以势必要引入生产者互斥锁和消费者互斥锁。

2.先加锁 or 先申请信号量

一定是先申请信号量,在加锁。先申请信号量,会让所有线程先瓜分好信号量,在其它线程进行等待锁的时候,此时资源已经申请好了,可以直接进入临界区;如果先加锁,在申请信号量,所有线程都会在锁上等待,等待完成之后在去申请信号量,这样解决问题的实际效率会变低。

3.使用信号量实现生产者消费者模型,对资源进行使用,申请时,为什么不判断一下条件是否满足?

信号量本身就是判断条件! 信号量:是一个计数器,是资源的预订机制。预订:可以理解为在外部,可以不判断资源是否满足,就可以知道内部资源的情况! 

相关文章:

Linux_线程同步生产者消费者模型

同步的相关概念 同步&#xff1a;在保证数据安全的前提下&#xff0c;让线程能够按照某种特定的顺序访问临界资源&#xff0c;从而有效避免饥饿问题&#xff0c;叫做同步竞态条件&#xff1a;因为时序问题&#xff0c;而导致程序异常&#xff0c;我们称之为竞态条件。 同步的…...

边缘检测算法(candy)

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 Canny 边缘检测的步骤 1. 灰度转换 如果输入的是彩色图像&#xff0c;则需要先转换为 灰度图像&#xff0c;因为边缘检测通常在单通道图像上进行。 2. 高斯滤波&#xff08;Gaussian Blur&#xff09; 由于边缘…...

Hot100之双指针

283移动零 题目 思路解析 那我们就把不为0的数字都放在数组前面&#xff0c;然后数组后面的数字都为0就行了 代码 class Solution {public void moveZeroes(int[] nums) {int left 0;for (int num : nums) {if (num ! 0) {nums[left] num;// left最后会变成数组中不为0的数…...

租房管理系统实现智能化租赁提升用户体验与运营效率

内容概要 在当今快速发展的租赁市场中&#xff0c;租房管理系统的智能化转型显得尤为重要。它不仅帮助房东和租客之间建立更高效的沟通桥梁&#xff0c;还优化了整个租赁流程。通过智能化技术&#xff0c;这套系统能够自动处理资产管理、合同签署、财务管理等所有关键环节。这…...

spring和Mybatis的逆向工程

在现代企业级开发中&#xff0c;使用Spring和MyBatis进行快速、高效的数据库操作是非常常见的。本文将深入探讨如何使用Spring和MyBatis进行逆向工程&#xff0c;帮助开发者自动生成数据库相关的代码&#xff0c;提高开发效率和代码质量。 一、什么是逆向工程 逆向工程是指从…...

计算机网络 IP 网络层 2 (重置版)

IP的简介&#xff1a; IP 地址是互联网协议地址&#xff08;Internet Protocol Address&#xff09;的简称&#xff0c;是分配给连接到互联网的设备的唯一标识符&#xff0c;用于在网络中定位和通信。 IP编制的历史阶段&#xff1a; 1&#xff0c;分类的IP地址&#xff1a; …...

松灵机器人 scout ros2 驱动 安装

必须使用 ubuntu22 必须使用 链接的humble版本 #打开can 口 sudo modprobe gs_usbsudo ip link set can0 up type can bitrate 500000sudo ip link set can0 up type can bitrate 500000sudo apt install can-utilscandump can0mkdir -p ~/ros2_ws/srccd ~/ros2_ws/src git cl…...

【Leetcode 每日一题】541. 反转字符串 II

问题背景 给定一个字符串 s s s 和一个整数 k k k&#xff0c;从字符串开头算起&#xff0c;每计数至 2 k 2k 2k 个字符&#xff0c;就反转这 2 k 2k 2k 字符中的前 k k k 个字符。 如果剩余字符少于 k k k 个&#xff0c;则将剩余字符全部反转。如果剩余字符小于 2 k…...

掌握API和控制点(从Java到JNI接口)_35 JNI开发与NDK 03

3、 如何载入 .so档案 VM的角色 由于Android的应用层级类别都是以Java撰写的&#xff0c;这些Java类别转译为Dex型式的Bytecode之后&#xff0c;必须仰赖Dalvik虚拟机器(VM: Virtual Machine)来执行之。 VM在Android平台里&#xff0c;扮演很重要的角色。此外&#xff0c;在执…...

解锁豆瓣高清海报(二) 使用 OpenCV 拼接和压缩

解锁豆瓣高清海报(二): 使用 OpenCV 拼接和压缩 脚本地址: 项目地址: Gazer PixelWeaver.py pixel_squeezer_cv2.py 前瞻 继上一篇“解锁豆瓣高清海报(一) 深度爬虫与requests进阶之路”成功爬取豆瓣电影海报之后&#xff0c;本文将介绍如何使用 OpenCV 对这些海报进行智…...

【C/C++】Windows SAPI自实现文字转语音

本文通过封装Windows SAPI&#xff08;Speech Application Programming Interface&#xff09;&#xff0c;提供了一个现代化的C接口实现文字转语音功能。主要特性包括支持同步/异步语音合成、可调节语速&#xff08;-10到10&#xff09;和音量控制&#xff08;0-100%&#xff…...

openmv的端口被拆分为两个 导致电脑无法访问openmv文件系统解决办法 openmv USB功能改动 openmv驱动被更改如何修复

我之前误打误撞遇到一次&#xff0c;直接把openmv的全部端口删除卸载然后重新插上就会自动重新装上一个openmv端口修复成功&#xff0c;大家可以先试试不行再用下面的方法 全部卸载再重新插拔openmv 要解决OpenMV IDE中出现的两个端口问题&#xff0c;可以尝试以下步骤&#x…...

8.攻防世界Web_php_wrong_nginx_config

进入题目页面如下 尝试弱口令密码登录 一直显示网站建设中&#xff0c;尝试无果&#xff0c;查看源码也没有什么特别漏洞存在 用Kali中的dirsearch扫描根目录试试 命令&#xff1a; dirsearch -u http://61.147.171.105:53736/ -e* 登录文件便是刚才登录的界面打开robots.txt…...

音叉模态分析

目录 0 序言 1 自由状态下模态求解 1.1 添加模态项目 1.2 生成网格 1.3 设置最大模态阶数 1.4 求解 1.5 结果查看 1.6 结果分析 2 音叉能否释放频率440Hz的音调 3 预应力模态求解 3.1 静态结构分析 3.1.1 添加静态结构项目 3.1.2生成网格 3.1.3添加边界条件 3.1…...

智慧园区系统集成解决方案引领未来城市管理的智能化转型

内容概要 在现代城市管理的背景下&#xff0c;“智慧园区系统集成解决方案”正扮演着越来越重要的角色。这种解决方案不仅仅是技术上的创新&#xff0c;更是一种全新的管理理念&#xff0c;它旨在通过高效的数据整合与分析&#xff0c;优化资源配置&#xff0c;提升运营效率。…...

(即插即用模块-特征处理部分) 二十、(TPAMI 2022) Permute-MLP 置换MLP模块

文章目录 1、Permute-MLP layer2、代码实现 paper&#xff1a;Vision Permutator: A Permutable MLP-Like Architecture for Visual Recognition Code&#xff1a;https://github.com/Andrew-Qibin/VisionPermutator 1、Permute-MLP layer 传统的 MLP-like 模型&#xff08;如…...

FBX SDK的使用:基础知识

Windows环境配置 FBX SDK安装后&#xff0c;目录下有三个文件夹&#xff1a; include 头文件lib 编译的二进制库&#xff0c;根据你项目的配置去包含相应的库samples 官方使用案列 动态链接 libfbxsdk.dll, libfbxsdk.lib是动态库&#xff0c;需要在配置属性->C/C->预…...

爬虫基础(二)Web网页的基本原理

一、网页的组成 网页由三部分构成&#xff1a;HTML、JavaScript、CSS。 &#xff08;1&#xff09;HTML HTML 相当于网页的骨架&#xff0c;它通过使用标签来定义网页内容的结构。 举个例子&#xff1a; 它把图片标签为img、把视频标签为video&#xff0c;然后组合到一个界面…...

1.4 Go 数组

一、数组 1、简介 数组是切片的基础 数组是一个固定长度、由相同类型元素组成的集合。在 Go 语言中&#xff0c;数组的长度是类型的一部分&#xff0c;因此 [5]int 和 [10]int 是两种不同的类型。数组的大小在声明时确定&#xff0c;且不可更改。 简单来说&#xff0c;数组…...

爬虫基础(三)Session和Cookie讲解

目录 一、前备知识点 &#xff08;1&#xff09;静态网页 &#xff08;2&#xff09;动态网页 &#xff08;3&#xff09;无状态HTTP 二、Session和Cookie 三、Session 四、Cookie &#xff08;1&#xff09;维持过程 &#xff08;2&#xff09;结构 正式开始说 Sessi…...

hive:基本数据类型,关于表和列语法

基本数据类型 Hive 的数据类型分为基本数据类型和复杂数据类型 加粗的是常用数据类型 BOOLEAN出现ture和false外的其他值会变成NULL值 没有number,decimal类似number 如果输入的数据不符合数据类型, 映射时会变成NULL, 但是数据本身并没有被修改 创建表 创建表的本质其实就是在…...

Maya软件安装步骤与百度网盘链接

软件简介&#xff1a; MAYA软件是Autodesk旗下的著名三维建模和动画软件。maya软件功能更为强大&#xff0c;体系更为完善&#xff0c;因此国内很多的三维动画制作人员都开始转向maya&#xff0c;maya软件已成为三维动画软件的主流。 百度网盘链接: https://pan.baidu.com/s…...

error: RPC failed; curl 56 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054

Descriptions&#xff1a; Solutions&#xff1a;...

PID算法的数学实现和参数确定方法

目录 概述 1 算法描述 1.1 PID算法模型 1.2 PID离散化的图形描述 1.3 PID算法的特点 2 离散化的PID算法 2.1 位置式PID算法 2.2 增量式PID算法 2.3 位置式PID与增量式PID比较 3 控制器参数整定 3.1 PID参数确定方法 3.1.1 凑试法 3.1.2 临界比例法 3.1.3 经验法…...

【已解决】ECharts 没有在页面生效

元素高度问题&#xff1a; ECharts 需要一个具有明确高度的容器来渲染图表&#xff0c;也就是说 echartsRef 元素需要一个明确的 div 高度。我项目中的问题就是在 .base-echarts&#xff08;我项目中的最外层元素&#xff09; 上设置了高度为 300px&#xff0c;但没有为 .echar…...

三角函数正交性应用--以信号分离为例

三角函数的正交性的应用 引言 在信号处理、通信等众多领域中&#xff0c;三角函数的正交性发挥着至关重要的作用。它不仅是理论分析的基础&#xff0c;更是实际应用中解决各种问题的有力工具。本文将详细介绍三角函数正交性的定义、证明&#xff0c;并通过一个具体的信号处理…...

ASP.NET Core 中使用依赖注入 (DI) 容器获取并执行自定义服务

目录 一、ASP.NET Core 中使用依赖注入 (DI) 容器获取并执行自定义服务 1. app.Services 2. GetRequiredService() 3. Init() 二、应用场景 三、依赖注入使用拓展 1、使用场景 2、使用步骤 1. 定义服务接口和实现类 2. 注册服务到依赖注入容器 3. 使用依赖注入获取并…...

一个数如果恰好等于他的因子之和,这是就成为“完数“,例如6=1+2+3.编程找出1000以内的所有完数

from sys import stdoutfor i in range(2,1001):k[] #用于存储因子si #初始化s为当前数字ifor j in range(1,i):if i%j0: #如果j是i的因子s-j #从s中减去银子jk.append(j) #将因子j加入列表kif s0:#如果s最终为0,说明i是一个完数print(i)for j in range(len(k)): #遍历银子列表…...

理解 InnoDB 如何处理崩溃恢复

在数据库领域&#xff0c;数据的一致性与可靠性至关重要。InnoDB 存储引擎的崩溃恢复机制是保障数据安全的核心&#xff0c;其中 Doublewrite Buffer 和 Redo Log 发挥着关键作用。下面&#xff0c;我们将详细探讨 InnoDB 从写入到崩溃恢复的全过程。 一、写入流程 修改页面&…...

Linux-CentOS的yum源

1、什么是yum yum是CentOS的软件仓库管理工具。 2、yum的仓库 2.1、yum的远程仓库源 2.1.1、国内仓库 国内较知名的网络源(aliyun源&#xff0c;163源&#xff0c;sohu源&#xff0c;知名大学开源镜像等) 阿里源:https://opsx.alibaba.com/mirror 网易源:http://mirrors.1…...

Redis复制

一、Redis复制&#xff1a; 1.为了解决分布式中单点故障问题&#xff0c;通常会把数据复制多个副本部署到其他机器上来解决故障恢复和负载均衡等需求。 2.建立复制 参与复制的redis实例划分为主节点&#xff08;master&#xff09;和从节点&#xff08;slave&#xff09;,每个从…...

小白怎样部署和使用本地大模型DeepSeek ?

1 安装Ollama &#xff08;1&#xff09; 下载Ollama 从https://ollama.com/download 下载, 根据你的系统下载对应的版本&#xff0c;支持Win, Mac ,Linux&#xff1a; 如果下载不了&#xff0c;右键复制链接地址&#xff0c;贴到迅雷&#xff0c;指定可以下载下来。 (2) 安…...

ECharts 样式设置

ECharts 样式设置 引言 ECharts 是一款功能强大的可视化库&#xff0c;广泛用于数据可视化。样式设置是 ECharts 中的重要一环&#xff0c;它能够帮助开发者根据需求调整图表的视觉效果&#xff0c;使其更加美观和易于理解。本文将详细介绍 ECharts 的样式设置&#xff0c;包…...

Java synchronized关键字和锁分类

专栏系列文章地址&#xff1a;https://blog.csdn.net/qq_26437925/article/details/145290162 本文目标&#xff1a; 关于synchronized已经有很多博主很细致的讲解了&#xff0c;本文主要是通过代码例子再次理解下&#xff0c;并要求能头脑反射出相关知识点主要是锁分类的知识…...

Vue.js 新的生命周期钩子:`onMounted`, `onUpdated` 等

Vue.js 新的生命周期钩子&#xff1a;onMounted, onUpdated 等 今天我们来聊聊 Vue 3 中的生命周期钩子&#xff0c;特别是 onMounted、onUpdated 等。如果你对如何在 Vue 3 的组合式 API&#xff08;Composition API&#xff09;中使用这些钩子感到困惑&#xff0c;那么这篇文…...

OpenCV:闭运算

目录 1. 简述 2. 用膨胀和腐蚀实现闭运算 2.1 代码示例 2.2 运行结果 3. 闭运算接口 3.1 参数详解 3.2 代码示例 3.3 运行结果 4. 闭运算的应用场景 5. 注意事项 相关阅读 OpenCV&#xff1a;图像的腐蚀与膨胀-CSDN博客 OpenCV&#xff1a;开运算-CSDN博客 1. 简述…...

Android 音视频编解码 -- MediaCodec

引言 如果我们只是简单玩一下音频、视频播放&#xff0c;那么使用 MediaPlayer SurfaceView 播放就可以了&#xff0c;但如果想加个水印&#xff0c;加点其他特效什么的&#xff0c;那就不行了&#xff1b; 学习 Android 自带的硬件码类 – MediaCodec。 MediaCodec 介绍 Med…...

移动互联网用户行为习惯哪些变化,对小程序的发展有哪些积极影响

一、碎片化时间利用增加 随着生活节奏的加快&#xff0c;移动互联网用户的碎片化时间越来越多。在等公交、排队、乘坐地铁等间隙&#xff0c;用户更倾向于使用便捷、快速启动的应用来满足即时需求。小程序正好满足了这一需求&#xff0c;无需下载安装&#xff0c;随时可用&…...

数据分析系列--②RapidMiner导入数据和存储过程

一、下载数据 二、导入数据 1. 在本地计算机中创建3个文件夹 2. 从本地选择.csv或.xlsx 三、界面说明 四、存储过程 1.保存 Congratulations, you are done. 一、下载数据 点击下载AssociationAnalysisData.xlsx数据集 二、导入数据 1. 在本地计算机中创建3个文件夹 2. 从…...

Prometheus 中的 Exporter

在 Prometheus 生态系统中,Exporter 扮演着至关重要的角色,它们负责从不同的服务或系统中收集和暴露度量数据。本文将详细介绍 Exporter 的概念、类型以及如何有效使用它们将 Prometheus 集成到各种系统中进行监控。 什么是 Exporter? Exporter 是一段软件,它从应用程序或…...

从 HTTP/1.1 到 HTTP/3:如何影响网页加载速度与性能

一、前言 在最近使用Apipost时&#xff0c;突然注意到了http/1.1和http/2&#xff0c;如下图&#xff1a; 在我根深蒂固的记忆中&#xff0c;对于http的理解还停留在TCP协议、三次握手。由于我的好奇心&#xff0c;于是触发了我被动“开卷”&#xff0c;所以有了这篇文章&…...

GenAI 在金融服务领域的应用:2025 年的重点是什么

作者&#xff1a;来自 Elastic Karen Mcdermott GenAI 不是魔法 我最近参加了 ElasticON&#xff0c;我们与纽约 Elastic 社区一起度过了一天&#xff0c;讨论了使用检索增强生成 (retrieval augmented generation - RAG) 为大型语言模型 (large language models - LLMs) 提供…...

小红书文案生成器(基于openai API和streamlit)

prompt_text.py&#xff1a; # 专门用来放提示文本&#xff08;系统提示、用户提示&#xff09;system_prompt_text """ 你是小红书爆款写作专家&#xff0c;请你遵循以下步骤进行创作&#xff1a;首先产出5个标题&#xff08;包含适当的emoji表情&#xff09…...

Spring AOP 入门教程:基础概念与实现

目录 第一章&#xff1a;AOP概念的引入 第二章&#xff1a;AOP相关的概念 1. AOP概述 2. AOP的优势 3. AOP的底层原理 第三章&#xff1a;Spring的AOP技术 - 配置文件方式 1. AOP相关的术语 2. AOP配置文件方式入门 3. 切入点的表达式 4. AOP的通知类型 第四章&#x…...

力扣面试150 快乐数 循环链表找环 链表抽象 哈希

Problem: 202. 快乐数 &#x1f469;‍&#x1f3eb; 参考题解 Code public class Solution {public int squareSum(int n) {int sum 0;while(n > 0){int digit n % 10;sum digit * digit;n / 10;}return sum;}public boolean isHappy(int n) {int slow n, fast squa…...

【实战篇章】深入探讨:服务器如何响应前端请求及后端如何查看前端提交的数据

文章目录 深入探讨&#xff1a;服务器如何响应前端请求及后端如何查看前端提交的数据一、服务器如何响应前端请求HTTP 请求生命周期全解析1.前端发起 HTTP 请求&#xff08;关键细节强化版&#xff09;2. 服务器接收请求&#xff08;深度优化版&#xff09; 二、后端如何查看前…...

《AI大模型开发笔记》DeepSeek技术创新点

一、DeepSeek横空出世 DeepSeek V3 以颠覆性技术架构创新强势破局&#xff01;革命性的上下文处理机制实现长文本推理成本断崖式下降&#xff0c;综合算力需求锐减90%&#xff0c;开启高效 AI 新纪元&#xff01; 最新开源的 DeepSeek V3模型不仅以顶尖基准测试成绩比肩业界 …...

【大模型LLM面试合集】大语言模型架构_MHA_MQA_GQA

MHA_MQA_GQA 1.总结 在 MHA&#xff08;Multi Head Attention&#xff09; 中&#xff0c;每个头有自己单独的 key-value 对&#xff1b;标准的多头注意力机制&#xff0c;h个Query、Key 和 Value 矩阵。在 MQA&#xff08;Multi Query Attention&#xff09; 中只会有一组 k…...

实战技巧:如何快速增加网站的收录页面?

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/43.html 要快速增加网站的收录页面&#xff0c;可以从以下几个方面进行实战优化&#xff1a; 一、内容优化 高质量原创内容 确保网站内容具备高质量与原创性&#xff0c;满足搜索引擎对独…...

20-30 五子棋游戏

20-分析五子棋的实现思路_哔哩哔哩_bilibili20-分析五子棋的实现思路是一次性学会 Canvas 动画绘图&#xff08;核心精讲50个案例&#xff09;2023最新教程的第21集视频&#xff0c;该合集共计53集&#xff0c;视频收藏或关注UP主&#xff0c;及时了解更多相关视频内容。https:…...