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

线程的同步

目录

引入

认识条件变量 

快速认识接口​编辑

认识条件变量​编辑

测试代码​编辑

生产消费模型

为何要使用生产者消费者模型

理解 

编写生产消费模型

BlockingQueue

单生产单消费 

多生产多消费 

引入

同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步  

认识条件变量 

条件变量是C语言中使用POSIX线程(pthread)库实现线程同步的一种机制。它允许线程在某个条件满足之前进入等待状态,直到其他线程通知它们该条件已改变。

快速认识接口

认识条件变量

测试代码

一次唤醒一个:

一次全部唤醒: 

#include <iostream>
#include <string>
#include<unistd.h>
#include <pthread.h>
using namespace std;
pthread_mutex_t gmutex=PTHREAD_MUTEX_INITIALIZER;//定义一把全局的锁
pthread_cond_t gcond=PTHREAD_COND_INITIALIZER;//全局条件变量
int num = 5;void* waitt(void*agv){string name=static_cast<const char*>(agv);while(true){pthread_mutex_lock(&gmutex);//加锁 pthread_cond_wait(&gcond,&gmutex);//条件变量等待,这里就是线程等待的位置usleep(10000);cout<<"i am: "<<name<<endl;pthread_mutex_unlock(&gmutex);//解锁}
}
int main()
{pthread_t threads[num];for (int i = 0; i < num; i++)//创建线程{char *name = new char[1024];              // 用来动态分配一个大小为1024字节的字符数组,并将其地址赋给指针namesnprintf(name, 1024, "thread-%d", i + 1); // 注意不用sizeof了因为sizeof(name)为地址字节不是大小了pthread_create(threads + i, nullptr, waitt, (void *)name);usleep(10000);}sleep(1);while(true){//唤醒pthread_cond_broadcast(&gcond);//全部唤醒//pthread_cond_signal(&gcond);//一次一个cout<<"唤醒一个线程......"<<endl;sleep(2);//唤醒的慢些}for (int i = 0; i < num; i++)//线程等待{           pthread_join(threads[i],nullptr);}
}

生产消费模型

为何要使用生产者消费者模型

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

理解 

生产消费模型通常是多执行流并发的模型------多个执行流之间怎么进行互斥同步之间如何协同的模型;

供应商和消费者就是线程,超市就是一段内存,方便面就是数据;

未来生产线程将数据放到缓存中,消费者需要的时候从缓存拿;

 思考切入点:“321”原则

                     1.  一个交易场所(特定数据结构形式存在的一段内存空间)

                     2.  两种角色(生产角色,消费角色)--生产线程,消费线程

                     3.  3种关系(生产和生产(竞争关系-互斥),消费和消费(互斥--资源少时就竞争了),生产和消费(互斥--生产者在向缓冲区写入数据时,消费者不能同时从缓冲区中读取数据,以免读取错误数据&&同步--消费者消费数据导致没数据就通知生产者放数据))

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

编写生产消费模型

BlockingQueue

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

那么可以想到进程间通信时用的管道,一个向管道里写另一个从管道里读。管道如果满了,写进程就要阻塞,如果空了,读进程就要阻塞。管道就是典型的阻塞队列--生产消费模型,只不过使用进程来代替;

单生产单消费 

那么生产和生产以及消费和消费之间的关系就不用维护了;

简单测试代码

blockqueue.hpp:

#pragma once
#include <iostream>
#include <string>
#include <queue> //使用stl的queue
#include <pthread.h>using namespace std;
const static int defaultcap = 5;template <typename T> // 引入模板
class blockqueue
{
private:bool full(){return _block_queue.size() == _max_cap;}bool empty(){return _block_queue.empty();}public:blockqueue(int cap = defaultcap) : _max_cap(cap){pthread_mutex_init(&_mutex, nullptr); // 初始化锁pthread_cond_init(&_p_cond, nullptr); // 初始条件变量pthread_cond_init(&_c_cond, nullptr); // 初始条件变量}void Pop(T *out){                                // 把队列中的数据带出去pthread_mutex_lock(&_mutex); // 用的同一把锁,互斥关系while (empty()){pthread_cond_wait(&_c_cond, &_mutex); // 在该条件变量下等待}// 此时走到这里,1.没有满 || 2.被唤醒了*out = _block_queue.front();_block_queue.pop(); // 拿出数据了pthread_mutex_unlock(&_mutex);pthread_cond_signal(&_p_cond); // 消费一个了那么你就可以生产了,队列有空间了// signal放在unlock之前还是之后都是可以的}// 那么唤醒可以由他们两个互相唤醒互相void equeue(const T &in){                                // 入队列pthread_mutex_lock(&_mutex); // 加锁防止向临界区放数据被打扰while (full()){ // 判断是否为满了// 满了不能生产,必须等待// 此时在临界区里,加锁和解锁之间// pthread_cond_wait在被调用的时候:除了让自己继续排队等待,还会自己释放传入的锁// 函数返回的时候,不就还在临界区么?那么被返回时必须先参与锁的竞争,重新加上锁,该函数才被返回;那么返回时就有锁了pthread_cond_wait(&_p_cond, &_mutex); // 在该条件变量下等待}// 此时走到这里,1.没有满 || 2.被唤醒了_block_queue.push(in); // 生产到阻塞队列里,此时没解锁所以一定至少有一个数据在队列里pthread_mutex_unlock(&_mutex);pthread_cond_signal(&_c_cond); // 生产一个了那么你就可以消费了// 让消费者消费}~blockqueue(){pthread_mutex_destroy(&_mutex); // 将锁销毁pthread_cond_destroy(&_p_cond); // 将局部条件变量销毁pthread_cond_destroy(&_c_cond); // 将局部条件变量销毁}private:queue<T> _block_queue;  // 临界资源int _max_cap;           // 队列最大容量pthread_mutex_t _mutex; // 锁pthread_cond_t _p_cond; // 为生产者提供的条件变量pthread_cond_t _c_cond; // 为消费者提供的条件变量
};

 main.cc:

构建数据(int)

#include"blockqueue.hpp"
#include<pthread.h>
#include<ctime>
#include<unistd.h>void*consumer(void*agv){blockqueue<int>* bq=static_cast<blockqueue<int>* >(agv);while(true){sleep(2);//消费的慢些//1.拿数据int date=0;bq->Pop(&date);//拿//2.处理数据cout<<"consumer-> "<<date<<endl;}
}
void*productor(void*agv){srand(time(nullptr)^getpid());//增加随机性blockqueue<int>* bq=static_cast<blockqueue<int>* >(agv);//两个线程看到同一个阻塞队列while(true){//1.构建数据int date=rand()%10+1;//[1,10]//2.生产数据bq->equeue(date);//入cout<<"producter-> "<<date<<endl;}
}int main(){blockqueue<int>* bq=new blockqueue<int>();//使用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;
}// #include <iostream>
// #include <string>
// #include<unistd.h>
// #include <pthread.h>
// using namespace std;
// pthread_mutex_t gmutex=PTHREAD_MUTEX_INITIALIZER;//定义一把全局的锁
// pthread_cond_t gcond=PTHREAD_COND_INITIALIZER;//全局条件变量
// int num = 5;// void* waitt(void*agv){
//     string name=static_cast<const char*>(agv);
//     while(true){
//         pthread_mutex_lock(&gmutex);//加锁 
//         pthread_cond_wait(&gcond,&gmutex);//条件变量等待,这里就是线程等待的位置
//             usleep(10000);
//     cout<<"i am: "<<name<<endl;
//     pthread_mutex_unlock(&gmutex);//解锁
//     }
// }
// int main()
// {
//     pthread_t threads[num];
//     for (int i = 0; i < num; i++)//创建线程
//     {
//         char *name = new char[1024];              // 用来动态分配一个大小为1024字节的字符数组,并将其地址赋给指针name
//         snprintf(name, 1024, "thread-%d", i + 1); // 注意不用sizeof了因为sizeof(name)为地址字节不是大小了
//         pthread_create(threads + i, nullptr, waitt, (void *)name);
//         usleep(10000);
//     }
//     sleep(1);
//     while(true){//唤醒
//         pthread_cond_broadcast(&gcond);//全部唤醒
//         //pthread_cond_signal(&gcond);//一次一个
//         cout<<"唤醒一个线程......"<<endl;
//         sleep(2);//唤醒的慢些
//     }//     for (int i = 0; i < num; i++)//线程等待
//     {           
//         pthread_join(threads[i],nullptr);
//     }
// }

main.cc: 

构建任务(task--类)

#include"blockqueue.hpp"
#include"task.hpp"
#include<pthread.h>
#include<ctime>
#include<unistd.h>void*consumer(void*agv){blockqueue<task>* bq=static_cast<blockqueue<task>* >(agv);while(true){sleep(2);//消费的慢些//1.拿数据task t;//无参构造bq->Pop(&t);//拿//2.处理数据t.excute();cout<<"consumer-> "<<t.result()<<endl;}
}
void*productor(void*agv){srand(time(nullptr)^getpid());//增加随机性blockqueue<task>* bq=static_cast<blockqueue<task>* >(agv);//两个线程看到同一个阻塞队列while(true){//1.构建数据int x=rand()%10+1;//[1,10]usleep(10000);//让两个数据尽量不一样,休眠一段时间int y=rand()%10+1;//[1,10]//2.生产数据task t(x,y);//有参构造bq->equeue(t);//入cout<<"producter-> "<<t.debug()<<endl;}
}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;
}

task.hpp:

#pragma once
#include "blockqueue.hpp"
using namespace std;
class task
{
public:task() {}task(int x, int y) : _x(x), _y(y){}void excute(){_result = _x + _y;}string debug(){string msg = to_string(_x) + "+" + to_string(_y) + "=?";return msg;}string result(){string msg = to_string(_x) + "+" + to_string(_y) +"="+ to_string(_result);return msg;}~task(){}private:int _x;int _y;int _result;
};

main.cc:

仿函数

#include "blockqueue.hpp"
#include "task.hpp"
#include <pthread.h>
#include <ctime>
#include <unistd.h>void *consumer(void *agv)
{blockqueue<task_t> *bq = static_cast<blockqueue<task_t> *>(agv);while (true){sleep(2); task_t t;bq->Pop(&t);// 从队列中取出并执行任务t();// 执行任务}
}
void *productor(void *agv)
{srand(time(nullptr) ^ getpid());                             // 增加随机性blockqueue<task_t> *bq = static_cast<blockqueue<task_t> *>(agv); // 两个线程看到同一个阻塞队列while (true){bq->equeue(download);   cout<<"productor->download "<<endl;}
}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;
}

task.hpp:

#pragma once
#include<iostream>
#include<functional>
using namespace std;using task_t=function<void()>;//等价于typedef function<void()> task_t;
//定义了一个新的类型别名 task_t,它表示一个接受无参数并返回 void 的函数类型void download(){cout<<"i am a download task"<<endl;
}

多生产多消费 

多生产多消费直接复用上面代码即可;

那么针对生产者不仅仅要将任务放到超市(花费时间),他还要产生任务也要花费时间;

那么针对消费者不仅仅要从超市拿到任务(花费时间),这个任务属于消费者自己了,那么他还要处理任务也要花费时间;

那么我们就不能仅仅只考虑放任务到超市叫生产,拿任务叫消费;

 那么一个生产商再放任务的时候,那么其他生产商有没有正在生产任务呢。

那么放任务和产生任务就并发了;

 那么一个消费者再拿任务的时候,那么其他消费者有没有早都获取了任务正在处理任务呢。

那么拿任务和处理任务就并发了;

如果未来消费处理任务花费时间比较久但是生产任务比较快,那么可以单生产多消费,那么生产任务的时候,一方面有线程在获取另一方面在并发处理任务;

如果未来生产任务花费时间比较久但是消费任务比较快,那么可以多生产单消费;

都很慢就可以多生产多消费;

问题:为什么等待就要在加锁和解锁之间等待呢?

 无论是生产者还是消费者都必须先检测资源的状态,对于生产和消费来说他们要访问公共资源,它不知道资源的条件是否满足。对于生产者来说希望队列有空间,对于消费者来说希望队列有数据。可是对于他们来说他们并不知道,只有他们查一次才知道,而查这一行为本身就是访问,就决定了查之前就要加锁,并且检测可能满足可能不满足,注定了必须在临界区里等待因为判定结果是在临界区里的;

相关文章:

线程的同步

目录 引入 认识条件变量 快速认识接口​编辑 认识条件变量​编辑 测试代码​编辑 生产消费模型 为何要使用生产者消费者模型 理解 编写生产消费模型 BlockingQueue 单生产单消费 多生产多消费 引入 同步&#xff1a;在保证数据安全的前提下&#xff0c;让线程…...

【启明智显分享】ZX7981PG/ZX7981PM融入官方OpenWrt,启明智显SDK/官方OpenWrt任由选择!

好消息&#xff01;好消息&#xff01;启明智显ZX7981PG和ZX7981PM正式融入官方 OpenWrt 的大家庭啦&#xff01;现在开发者不仅可以基于启明智显的SDK进行二次开发&#xff0c;还可以直接应用官方OpenWrt以及我们的开源资料进行开发&#xff01; 借助OpenWrt的强大生态&…...

如何用java发送包含表格形式的邮件

问题&#xff1a; 如何用java发送包含表格形式的邮件&#xff1f; 方法&#xff1a; 发用freemaker工具来替换html的表格变量&#xff0c;从而动态生成了html。然后再发送这个html格式&#xff08;不能用纯文本&#xff09;文本即可。 优化流程&#xff1a; 1、准备模板&#x…...

讲个故事:关于一次接口性能优化的心里路程

这是一个程序猿写的第一个故事&#xff0c;请各位懂行的客官静下心来&#xff0c;慢慢品读。就知道我为什么要单独写一个文章来记录这次过程了&#xff0c;因为实在是太坎坷了...... 背景介绍 近期项目投产时遇到一个问题&#xff0c;投产后在验证时发现大部分用户系统登…...

Centos7升级到openssh9.9

openssh9.9 是2024.9.20出的最新版ssh。因为客户扫描出一大堆centos7的漏洞&#xff0c;全是这个openssh的&#xff0c;好多补丁&#xff0c;所以索性升级到最新版。 需要自己制作rpm包&#xff0c;这个我是不懂&#xff0c;照这个来&#xff1a; Linux服务器升级openssh9.9最…...

使用 STM32F407 串口实现 485 通信

准备工作 了解485通信基本概念与原理&#xff1a;RS485通信详解_485通讯de接什么口-CSDN博客 安装编译软件&#xff1a;keil uVision 5.6 软件资料&#xff1a;STM32CubeF4 固件包&#xff0c;正点原子RS485通信例程 ​​​​​​​参考视频&#xff1a;第26讲 基础篇-新建H…...

基于NERF技术重建学习笔记

NeRF&#xff08;Neural Radiance Fields&#xff09;是一种用于3D场景重建的神经网络模型&#xff0c;能够从2D图像生成逼真的3D渲染效果。它将场景表征为一个连续的5D函数&#xff0c;利用了体积渲染和神经网络的结合&#xff0c;通过学习光线穿过空间时的颜色和密度来重建场…...

webView 支持全屏播放

webView 支持全屏播放 直接上代码 public class CustomFullScreenWebViewClient extends WebChromeClient {WebView webView;Context context;/*** 视频全屏参数*/protected static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS new FrameLayout.LayoutParams(ViewG…...

QGIS之三十二DEM地形导出三维模型gltf

效果 1、准备数据 (1)dem.tif (2)dom.tif 2、qgis加载dem和dom数据 3、安装插件 插件步骤可以参考这篇文章 QGIS之二十四安装插件 安装了Qgis2threejs插件,结果...

【python爬虫】携程旅行景点游客数据分析与可视化

一.选题背景 随着旅游业的快速发展&#xff0c;越来越多的人选择通过互联网平台预订旅行产品&#xff0c;其中携程网作为国内领先的在线旅行服务提供商&#xff0c;拥有大量的旅游产品和用户数据。利用爬虫技术可以获取携程网上各个景点的游客数据&#xff0c;包括游客数量、游…...

python实现onvif协议下控制摄像头变焦,以及融合人形识别与跟踪控制

#1024程序员节 | 征文# 这两天才因为项目需要&#xff0c;对网络摄像头的视频采集以及实现人形识别与跟踪技术。对于onvif协议自然起先也没有任何的了解。但是购买的摄像头是SONY网络头是用在其他地方的。因为前期支持探究项目解决方案&#xff0c;就直接拿来做demo测试使用。 …...

【Vue】Vue3.0(十四)接口,泛型和自定义类型的概念及使用

上篇文章&#xff1a; 【Vue】Vue3.0&#xff08;十三&#xff09;中标签属性ref&#xff08;加在普通标签上、加在组件标签上&#xff09;、局部样式 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Vue专栏&#xff1a;点击&#xff01; ⏰️创作时间&…...

【C++】红黑树万字详解(一文彻底搞懂红黑树的底层逻辑)

目录 00.引入 01.红黑树的性质 02.红黑树的定义 03.红黑树的插入 1.按照二叉搜索树的规则插入新节点 2.检测新节点插入后&#xff0c;是否满足红黑树的性质 1.uncle节点存在且为红色 2.uncle节点不存在 3.uncle节点存在且为黑色 04.验证红黑树 00.引入 和AVL树一样&am…...

开源FluentFTP实操,操控FTP文件

概述&#xff1a;通过FluentFTP库&#xff0c;轻松在.NET中实现FTP功能。支持判断、创建、删除文件夹&#xff0c;判断文件是否存在&#xff0c;实现上传、下载和删除文件。简便而强大的FTP操作&#xff0c;提升文件传输效率。 在.NET中&#xff0c;使用FluentFTP库可以方便地…...

论文解读 | ECCV2024 AutoEval-Video:一个用于评估大型视觉-语言模型在开放式视频问答中的自动基准测试...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 作者简介 陈修元&#xff0c;上海交通大学清源研究院硕士生 概述 总结来说&#xff0c;我们提出了一个新颖且具有挑战性的基准测试AutoEvalVideo&#xff0c;用于全…...

postgresql14主从同步流复制搭建

1. 如果使用docker搭建请移步 Docker 启动 PostgreSQL 主从架构&#xff1a;实现数据同步的高效部署指南_docker安装postgresql主从同步-CSDN博客 2. 背景 pgsql版本&#xff1a;PostgreSQL 14.13 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4…...

企业信息化管理中的数据集成方案:销售出库单对接

企业信息化管理中的数据集成方案&#xff1a;销售出库单对接 销售出库单旺店通→金蝶&#xff1a;高效数据集成案例分享 在企业信息化管理中&#xff0c;数据的高效流动和准确对接是实现业务流程自动化的关键。本文将聚焦于一个具体的系统对接集成案例&#xff1a;如何将旺店通…...

3.cpp基本数据类型

cpp基本数据类型 1.cpp基本数据类型 1.cpp基本数据类型 C基本数据类型和C语言的基本数据类型差不多 注意bool类型&#xff1a;存储真值 true 或假值 false&#xff0c;C语言编译器C99以上支持。 C语言的bool类型&#xff1a;要添加 #include <stdbool.h>头文件 #includ…...

MCK主机加固与防漏扫的深度解析

在当今这个信息化飞速发展的时代&#xff0c;网络安全成为了企业不可忽视的重要议题。漏洞扫描&#xff0c;简称漏扫&#xff0c;是一种旨在发现计算机系统、网络或应用程序中潜在安全漏洞的技术手段。通过自动化工具&#xff0c;漏扫能够识别出系统中存在的已知漏洞&#xff0…...

《软件估算之原始功能点:精准度量软件规模的关键》

《软件估算之原始功能点&#xff1a;精准度量软件规模的关键》 一、软件估算的重要性与方法概述二、原始功能点的构成要素&#xff08;一&#xff09;数据功能&#xff08;二&#xff09;事务功能 三、原始功能点的估算方法&#xff08;一&#xff09;功能点分类估算&#xff0…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...