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

[muduo] ThreadPool | TcpClient | 异步任务 | 通信测试

第九章:线程池(ThreadPool)

在第八章《TcpServer》中,我们了解muduo::net::TcpServer通过EventLoop线程池处理入站连接

这些EventLoop线程主要负责网络I/O:套接字读写和定时器处理,由PollerChannel协调,保持高效响应。

但当EventLoop线程中的MessageCallback函数需要执行耗时操作时会发生什么?

例如:

  • 复杂计算
  • 数据库访问(可能阻塞等待结果)
  • 大文件磁盘写入(可能阻塞)
  • 同步网络请求其他服务

若直接在EventLoop线程执行这些操作,线程将被阻塞,导致该线程管理的所有连接无法处理其他事件,服务器响应能力下降甚至引发超时。

这正是muduo::ThreadPool的价值所在。

线程池解决的问题

核心目标是可能阻塞或耗时的计算任务从关键EventLoop线程卸载

EventLoop线程必须保持空闲以快速处理网络I/O。

ThreadPool提供专用线程组执行任意任务。当EventLoop回调中遇到耗时任务时,将其提交给线程池,由池中的工作线程异步处理,使EventLoop线程快速返回继续处理网络事件。

类比场景:

  • EventLoop线程如同高效的前台接待员,仅处理简短交互(如引导访客、接收邮件)。
  • ThreadPool则是后勤团队,处理复杂请求(如审核详细申请),前台人员可立即返回岗位继续接待。
  • (类似于redismysql实现分类,还有上一篇文章当中的reactor分离,也是一个前台+多个处理人员)

ThreadPool:后台任务执行引擎

muduo::ThreadPool类管理一组待命的工作线程,关键设计要素包括:

  1. 线程集合:使用muduo::Thread类(第二章:线程)创建指定数量的工作线程
  2. 任务队列:线程安全的双端队列(std::deque),通过互斥锁(MutexLock)和条件变量(Condition)实现同步
  3. 工作线程逻辑:每个线程循环执行:取任务→执行→等待新任务
  4. 任务提交(run():通过run()方法将函数对象加入队列
  5. 阻塞等待(take():队列空时,工作线程通过条件变量(notEmpty_)阻塞休眠
  6. 唤醒机制:添加新任务时通过notEmpty_.notify()唤醒等待线程
  7. 有界队列(可选):可设置队列最大容量(maxQueueSize_),满时run()可能阻塞(通过notFull_),防止任务积压
  8. 启停控制start()启动线程池,stop()停止并等待线程退出

使用ThreadPool执行异步任务

通过创建实例、启动线程池并调用run()提交任务即可使用:

#include "muduo/base/ThreadPool.h"
#include "muduo/base/CurrentThread.h" 
#include "muduo/base/CountDownLatch.h" 
#include <cstdio>
#include <string>
#include <unistd.h>
#include <functional>// 工作线程中运行的函数
void print(const std::string& msg) {printf("线程池任务: %s, 进程ID: %d, 线程ID: %d\n",msg.c_str(), getpid(), muduo::CurrentThread::tid());sleep(1); // 模拟耗时操作
}int main() {printf("主线程启动. 进程ID: %d, 线程ID: %d\n", getpid(), muduo::CurrentThread::tid());muduo::ThreadPool pool("MyWorkerPool"); // 1. 创建线程池pool.setMaxQueueSize(5);               // 设置队列最大容量为5pool.start(3);                          // 2. 启动3个工作线程sleep(1); // 等待线程启动printf("线程池已启动.\n");// 3. 提交10个任务for (int i = 0; i < 10; ++i) {char task_msg[32];snprintf(task_msg, sizeof task_msg, "任务%d", i);pool.run(std::bind(print, std::string(task_msg))); // 提交任务}printf("任务提交完成.\n");sleep(10); // 等待任务执行pool.stop(); // 4. 停止线程池printf("线程池已停止.\n");return 0;
}

(需包含Muduo头文件并链接库)

使用muduo网络库中的线程池组件处理并发任务的基本流程,包含四个关键操作:
创建线程池、启动线程、提交任务、停止线程池。

原理

线程池管理一组工作线程,避免频繁创建销毁线程的开销。

任务提交后进入队列,空闲线程自动获取任务执行。

muduo库的ThreadPool采用生产者-消费者模型,主线程提交任务(生产者),工作线程处理任务(消费者)。

解析

初始化阶段
  • muduo::ThreadPool pool("MyWorkerPool")创建名为"MyWorkerPool"的线程池实例。
    pool.setMaxQueueSize(5)限制任务队列最多容纳5个未处理任务,防止内存过度消耗。

    pool.start(3)启动3个常驻工作线程,这些线程会持续从任务队列获取任务执行。启动后立即进入等待任务状态。

任务提交阶段
  • 循环提交10个打印任务,每个任务绑定print函数和字符串参数。

    当任务提交速度超过处理速度时,后提交的任务会因队列满(max=5)而阻塞,直到有线程处理完队列任务。

    print函数显示任务信息、进程PID和线程TID,通过sleep(1)模拟耗时操作。注意线程ID由muduo::CurrentThread::tid()获取,这是muduo库提供的跨平台线程标识。

终止阶段

sleep(10)确保所有任务完成,pool.stop()安全停止线程池:停止接受新任务,等待正在执行的任务完成,最后回收线程资源。

执行效果说明

程序输出将显示:

  1. 主线程信息
  2. 3个工作线程循环执行10个任务(每个任务间隔1秒)
  3. 由于只有3个线程,10个任务需约4秒完成(3并发+队列等待)
  4. 最终线程池停止信息

该模式适用于需要批量处理短生命周期任务的场景,如网络请求处理、日志写入等I/O密集型操作。

运行
理解:货架上有五个空位,三个三个的往上放货物
在这里插入图片描述

执行流程:

  1. 主线程创建线程池并启动3个工作线程
  2. 工作线程启动后立即尝试从空队列取任务,进入阻塞等待
  3. 主循环提交10个任务,工作线程被唤醒并并发执行
  4. 输出显示不同线程ID执行任务,主线程快速返回
  5. 任务队列满时run()可能阻塞,直到有空闲
  6. stop()通知所有线程退出,等待清理

线程池内部机制

核心交互流程:
在这里插入图片描述
加锁到任务队列中,再加锁的执行任务

关键源码(简化版):

// ThreadPool.h
class ThreadPool : noncopyable {
public:using Task = std::function<void ()>; // 任务类型explicit ThreadPool(const string& name = "ThreadPool");void setMaxQueueSize(int maxSize);    // 设置队列容量void start(int numThreads);           // 启动线程void stop();                          // 停止线程void run(Task f);                     // 提交任务private:bool isFull() const;                  // 队列是否满Task take();                          // 获取任务void runInThread();                   // 工作线程主循环mutable MutexLock mutex_;             // 保护队列和状态Condition notEmpty_;                  // 非空条件变量Condition notFull_;                   // 非满条件变量std::deque<Task> queue_;              // 任务队列size_t maxQueueSize_ = 0;             // 队列最大容量bool running_ = false;                // 运行状态
};// ThreadPool.cc
void ThreadPool::start(int numThreads) {running_ = true;// 创建numThreads个工作线程threads_.reserve(numThreads);for (int i = 0; i < numThreads; ++i) {threads_.emplace_back(new Thread(std::bind(&ThreadPool::runInThread, this)));threads_.back()->start();}
}void ThreadPool::run(Task task) {if (threads_.empty()) {task(); // 无线程时直接执行} else {MutexLockGuard lock(mutex_);while (isFull() && running_) {notFull_.wait(); // 队列满时等待}queue_.push_back(std::move(task));notEmpty_.notify(); // 通知工作线程}
}ThreadPool::Task ThreadPool::take() {MutexLockGuard lock(mutex_);while (queue_.empty() && running_) {notEmpty_.wait(); // 队列空时等待}Task task;if (!queue_.empty()) {task = queue_.front();queue_.pop_front();if (maxQueueSize_ > 0) {notFull_.notify(); // 通知任务提交者}}return task;
}void ThreadPool::runInThread() {while (running_) {Task task(take()); // 循环取任务if (task) task();  // 执行任务}
}

⭕核心功能

任务队列管理
ThreadPool 使用 std::deque<Task> 作为任务队列,通过 maxQueueSize_ 控制队列容量。

任务类型为 std::function<void()>,表示无参数无返回值的可调用对象

线程工作流程

  • 启动时通过 start() 创建多个工作线程,每个线程执行 runInThread() 循环。
  • runInThread() 不断调用 take() 获取任务并执行。若队列为空,线程在 notEmpty_ 条件变量上等待。
  • take() 从队列头部取出任务,若队列从满变为非满,通知 notFull_ 唤醒可能阻塞的任务提交者。

任务提交逻辑

  • run() 提交任务时,若队列已满且线程池在运行,提交线程在 notFull_ 上等待。
  • 任务入队后通过 notEmpty_.notify() 唤醒一个工作线程。
  • 若线程池未启动或无工作线程,任务直接在当前线程执行。

同步机制

  • MutexLock 保护队列和状态变量(running_)。
  • 两个 Condition 变量(notEmpty_notFull_)实现生产者-消费者模型,避免忙等待。

代码设计简洁高效,适合处理大量短期任务,典型应用如网络服务器的请求处理。

总结

功能描述作用/优势
工作线程基于muduo::Thread的线程集合提供独立于I/O线程的任务执行环境
任务队列线程安全的双端队列存储std::function<void ()类型任务解耦任务提交与执行,支持异步处理
run()方法提交任务到队列,队列满时可能阻塞用户接口,确保任务安全加入队列
take()方法工作线程从队列取任务,队列空时阻塞核心任务获取机制,保证工作线程高效等待
runInThread()工作线程主循环,执行take()和任务定义工作线程行为,维持任务处理循环
互斥锁保护队列和运行状态确保多线程访问共享数据的正确性
条件变量notEmpty_notFull_协调线程间通信实现高效的任务调度与资源管理
有界队列通过maxQueueSize_限制队列容量防止任务堆积导致内存溢出,提供背压机制

结论

muduo::ThreadPool是Muduo库中处理异步任务的核心组件,通过线程安全的任务队列和工作线程池,有效隔离耗时操作与网络I/O处理。其价值体现在:

  1. 资源隔离:保护EventLoop线程免受阻塞,确保网络高吞吐
  2. 弹性扩展:通过线程数配置适应不同计算负载
  3. 流量控制:有界队列防止资源耗尽
  4. 简化并发封装底层线程同步细节,提供简洁API

下一章我们将探讨TcpClient类,了解如何主动连接远程服务并管理TCP连接

第十章:TcpClient


第10章:TcpClient

  • 在上一章第9章:线程池中,我们讨论了muduo::ThreadPool如何通过卸载阻塞任务来保持EventLoop线程的响应能力。

  • 在此之前,我们探讨了TcpServer(第8章:TcpServer),它允许使用EventLoop线程池构建监听并管理多个传入TCP连接的服务器。

但如果想编写主动连接到远程服务器的程序(而不是等待连接)呢?

这就是网络客户端的作用。

  • 需要一种方法来连接到特定地址和端口,处理连接建立的异步性,管理连接后的数据交换,并可能需要处理连接失败和重试。

  • 手动实现这些需要创建套接字、设置为非阻塞模式、发起connect(2)系统调用(在非阻塞模式下会立即返回但稍后完成连接)、使用EventLoopChannel监视套接字的可写性(表示连接完成)或错误,然后管理已连接的套接字

这非常复杂,尤其是在需要自动重连等功能时。

这正是muduo::net::TcpClient要解决的问题。


TcpClient 解决了什么问题?

muduo::net::TcpClient为TCP连接的客户端提供了高级抽象。它处理以下完整流程:

  1. 发起连接:创建非阻塞套接字并调用connect()
  2. 监控连接状态:使用EventLoop等待连接成功建立或失败
  3. 管理连接:连接成功后,创建并管理单个TcpConnection对象(第6章:TcpConnection)来处理数据传输(发送和接收),使用其EventLoopBuffer(第7章:Buffer)
  4. 处理断开连接:在连接关闭时通知
  5. 自动重试(可选):如果启用,在初始连接失败或连接丢失时自动尝试重连

TcpClient想象成一位专门代表,其职责是呼叫另一个办公室(TcpServer)并建立单条通信线路。

  • 它处理拨号、等待对方接听以及通话接通后的线路管理。

  • 如果线路忙或通话中断,可以配置自动重拨。

TcpClient:连接发起者

muduo::net::TcpClient是在Muduo中创建TCP客户端应用程序的主要类。

以下是muduo::net::TcpClient的关键概念:

  1. 建立单一连接:与TcpServer不同,TcpClient对象设计用于建立和管理到一个特定远程服务器地址的连接,不处理多个传入连接
  2. 单个EventLoop拥有TcpClient对象必须在单个EventLoop线程中存在和使用,通常是客户端的主循环。所有回调和内部操作都在此循环线程中发生
  3. 拥有Connector:内部使用Connector对象,该组件负责异步connect()系统调用、监视套接字通道的连接完成/失败,并实现重试逻辑
  4. 拥有TcpConnection(连接时):当Connector成功建立连接后,TcpClient会为新套接字创建TcpConnection对象,所有数据I/O都通过该对象进行
  5. 使用相同回调:与TcpServer类似,通过熟悉的ConnectionCallbackMessageCallbackWriteCompleteCallback暴露连接生命周期和数据事件
  6. 连接/断开/停止控制:提供connect()disconnect()stop()方法控制客户端状态
  7. 自动重试enableRetry()方法允许在连接失败或丢失时启用自动重连

使用TcpClient:连接到回声服务器

让我们编写一个连接到第8章:TcpServer中构建的回声服务器的简单客户端。

该客户端将连接、发送消息、接收回声并断开连接。

需要:

  • 客户端的EventLoop
  • 服务器地址的InetAddress
  • 回调函数
#include "muduo/net/TcpClient.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/InetAddress.h"
#include "muduo/base/Logging.h"  // 用于LOG_INFO
#include <cstdio>  // 用于printf
#include <string>  // 用于std::string// 我们*单条连接*的TcpConnectionPtr(shared_ptr指向TcpConnection)
muduo::net::TcpConnectionPtr clientConnection;// 示例连接回调
void onConnection(const muduo::net::TcpConnectionPtr& conn) {LOG_INFO << "客户端连接 " << (conn->connected() ? "建立" : "断开");if (conn->connected()) {// 连接建立时存储连接指针clientConnection = conn;LOG_INFO << "已连接到 " << conn->peerAddress().toIpPort();// 发送初始消息std::string message = "你好,来自客户端!\n";printf("发送: %s", message.c_str());conn->send(message);} else {// 连接断开,清空存储的指针clientConnection.reset();LOG_INFO << "已断开与 " << conn->peerAddress().toIpPort() << " 的连接";// 实际应用中可在此处调用loop.quit()或处理重连conn->getLoop()->quit();  // 简单示例:断开时退出循环}
}// 示例消息回调(处理回声数据)
void onMessage(const muduo::net::TcpConnectionPtr& conn,muduo::net::Buffer* buf,muduo::Timestamp receiveTime) {LOG_INFO << "从 " << conn->name() << " 接收到 " << buf->readableBytes() << " 字节";// 将回声数据转为字符串std::string msg = buf->retrieveAllAsString();printf("收到回声消息: %s", msg.c_str());// 简单示例:收到回声后发送下一条消息// 实际协议中应处理消息并决定是否响应// std::string nextMessage = "另一条消息...\n";// conn->send(nextMessage); // 按需持续发送
}int main() {LOG_INFO << "main(): 进程ID = " << getpid();muduo::net::EventLoop loop;  // 客户端的事件循环// 服务器地址:localhost (127.0.0.1),端口9988muduo::net::InetAddress serverAddr("127.0.0.1", 9988);// 创建TcpClient实例muduo::net::TcpClient client(&loop, serverAddr, "EchoClient");// --- 配置客户端 ---// 设置回调client.setConnectionCallback(onConnection);client.setMessageCallback(onMessage);// 可选:启用连接失败/断开自动重连// client.enableRetry();// LOG_INFO << "已启用重连";// --- 启动连接过程 ---printf("启动客户端,正在连接到 %s...\n", serverAddr.toIpPort().c_str());client.connect();// --- 运行客户端循环 ---printf("运行客户端循环...\n");loop.loop();  // 阻塞直到调用loop.quit()// loop.loop()在连接关闭且onConnection调用loop->quit()后退出printf("客户端已停止。main()退出。\n");return 0;
}

(需要包含必要的Muduo头文件并链接库)

这段代码是使用muduo网络库(一个高性能C++网络库)实现的TCP客户端程序,负责连接到指定服务器,发送初始消息 并接收服务器返回的数据(回声)。

关键组件说明

必要头文件

  • TcpClient.h:客户端核心类
  • EventLoop.h:事件循环处理类
  • InetAddress.h:网络地址封装类
  • Logging.h:日志输出工具

主要执行流程

全局连接对象

muduo::net::TcpConnectionPtr clientConnection; // 保存当前有效连接

连接状态回调

void onConnection(const muduo::net::TcpConnectionPtr& conn) {// 连接建立时:// 1. 存储连接对象// 2. 发送初始问候消息// 连接断开时:// 1. 清除连接对象// 2. 退出事件循环
}

数据接收回调

void onMessage(/*参数略*/) {// 1. 读取接收缓冲区数据// 2. 打印接收内容(示例为回声服务)// 3. 可扩展发送后续消息
}

主程序逻辑

int main() {// 1. 创建事件循环对象// 2. 指定服务器地址(127.0.0.1:9988)// 3. 创建客户端实例// 4. 绑定回调函数// 5. 发起连接请求// 6. 运行事件循环(持续处理网络事件)
}

运行

  1. 客户端启动后自动连接服务器
  2. 连接成功后立即发送"你好,来自客户端!"
  3. 接收服务器返回的相同消息(回声)
  4. 断开连接时自动退出程序

注:实际使用时需配合对应的回声服务器运行,示例默认使用本地127.0.0.1:9988

在这里插入图片描述

main()关键部分解析:

  • muduo::net::EventLoop loop;:创建客户端的单一事件循环
  • muduo::net::InetAddress serverAddr("127.0.0.1", 9988);:指定服务器地址
  • muduo::net::TcpClient client(...);:创建关联事件循环和服务器地址的客户端实例
  • client.setConnectionCallback(...);:设置连接状态变更回调
  • client.connect();:启动异步连接过程
  • loop.loop();:启动事件循环

TcpClient内部机制:Connector与状态管理

TcpClient依赖内部Connector对象管理连接建立阶段和重试,同时管理状态标志和用于保存连接的shared_ptr

调用client.connect()时的流程:

在这里插入图片描述


核心源码解析

TcpClient.h关键成员(详见附带的代码):

class TcpClient : noncopyable {public:// 构造函数TcpClient(EventLoop* loop, const InetAddress& serverAddr, const string& nameArg);~TcpClient();void connect();    // 启动连接void disconnect(); // 优雅断开void stop();       // 停止连接和重试// 设置用户回调void setConnectionCallback(ConnectionCallback cb);void setMessageCallback(MessageCallback cb);private:EventLoop* loop_;ConnectorPtr connector_;  // 连接处理器TcpConnectionPtr connection_; // 当前连接// ... 其他成员
};

Connector类(详见附带的Connector.h/cc)关键方法:

  • start():由TcpClient::connect()调用,加入事件循环队列
  • handleWrite():处理连接完成事件
  • retry():实现带退避策略的重连机制

总结

功能描述优势
连接发起器启动与远程服务器的连接过程提供高级连接建立抽象
EventLoop绑定完全在单个事件循环线程内运行简化并发控制
Connector组件处理异步connect和重试逻辑封装复杂的状态管理
单一连接管理通过TcpConnection管理活动连接简化客户端连接管理逻辑
标准回调接口使用与TcpServer相同的回调机制统一编程模型
自动重试机制支持可配置的退避重试策略提升客户端容错能力

结论

muduo::net::TcpClient是构建Muduo客户端应用的核心抽象。

  • 通过内部Connector封装异步连接建立的复杂性,并通过标准TcpConnection对象管理通信。

  • 通过回调机制定义连接生命周期行为,配合可选的重试功能,可轻松构建高可靠的网络客户端。

  • 单线程设计模型与TcpServer形成完美互补,共同构成Muduo网络库的核心架构。

完结撒花~

test code: https://github.com/lvy010/Common-C-_Lib/tree/main/test

相关文章:

[muduo] ThreadPool | TcpClient | 异步任务 | 通信测试

第九章&#xff1a;线程池&#xff08;ThreadPool&#xff09; 在第八章《TcpServer》中&#xff0c;我们了解到muduo::net::TcpServer通过EventLoop线程池处理入站连接。 这些EventLoop线程主要负责网络I/O&#xff1a;套接字读写和定时器处理&#xff0c;由Poller和Channel…...

探索 Vue 替代方案

Vue vs React vs Angular 在快速迭代的前端世界&#xff0c;Vue、React和Angular三大框架的竞争从未停止。2025年的今天&#xff0c;它们各自进化出了怎样的面貌&#xff1f;让我们深入剖析它们的核心差异&#xff0c;助你做出明智的技术选型。 Vue.js 完整的基于组件的UI框…...

大模型的开发应用(十二):RAG 与 LlamaIndex基础

这里写目录标题 1 LlamaIndex 简要介绍1.1 核心价值1.2 核心组件1.3 核心流程1.4 为什么要用 LlamaIndex&#xff1f;1.5 典型应用场景1.6 与类似工具对比1.7 安装1.8 学习资源 2 文档解析与 Document 对象2.1 示例文件与代码2.2 Document 对象的核心特性2.3 在 RAG 工作流程中…...

Java面试题025:一文深入了解数据库Redis(1)

欢迎大家关注我的JAVA面试题专栏&#xff0c;该专栏会持续更新&#xff0c;从原理角度覆盖Java知识体系的方方面面。 一文吃透JAVA知识体系&#xff08;面试题&#xff09;https://blog.csdn.net/wuxinyan123/category_7521898.html?fromshareblogcolumn&sharetypeblogco…...

Web攻防-XSS跨站Cookie盗取数据包提交网络钓鱼BEEF项目XSS平台危害利用

知识点&#xff1a; 1、Web攻防-XSS跨站-手工代码&框架工具&在线平台 2、Web攻防-XSS跨站-Cookie盗取&数据提交&网络钓鱼 演示案例-WEB攻防-XSS跨站-Cookie盗取&数据提交&网络钓鱼&Beef工具 1、XSS跨站-攻击利用-凭据盗取 条件&#xff1a;无防…...

(LeetCode 面试经典 150 题) 169. 多数元素(哈希表 || 二分查找)

题目&#xff1a;169. 多数元素 方法一&#xff1a;二分法&#xff0c;最坏的时间复杂度0(nlogn)&#xff0c;但平均0(n)即可。空间复杂度为0(1)。 C版本&#xff1a; int nnums.size();int l0,rn-1;while(l<r){int mid(lr)/2;int ans0;for(auto x:nums){if(xnums[mid]) a…...

71、单元测试-Junit5简介

71、单元测试-Junit5简介 # JUnit 5 简介 JUnit 5 是 Java 平台上最流行的单元测试框架之一&#xff0c;是 JUnit 的重大升级版本&#xff0c;引入了许多新特性和改进&#xff0c;旨在提供更现代化、灵活和强大的测试体验。 ## 主要组成部分 JUnit 5 由三个模块组成&#xff1a…...

IEC61850 一致性测试中的 UCA 测试

一、IEC61850 与 UCA 的关系背景 标准演进&#xff1a;IEC61850 是电力系统自动化领域的国际通信标准&#xff0c;其发展与美国 UCA&#xff08;User Communications Architecture&#xff09;标准密切相关。2001 年&#xff0c;UCA 国际用户组织与 IEC 合作&#xff0c;将 UC…...

ProtoBuf:proto3 语法详解

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;ProtoBuf 在语法详解部分&#xff0c;依旧使⽤项⽬推进的⽅式完成讲解。这个部分会对通讯录进⾏多次升级&#xff0c;使⽤2.x表⽰升级的版本&#xff0c;最终将会升级如下内容&#xff1a; 不再打…...

博图SCL语言GOTO语句深度解析:精准跳转

在SCL编程中&#xff0c;**GOTO语句**是控制流程的底层工具&#xff0c;它允许程序无条件跳转到指定的**标签位置**。虽然现代编程中较少使用&#xff0c;但在特定工业场景下仍能发挥独特价值。 GOTO语句核心机制 基本语法结构 // 定义标签 <标签名>: // 跳转指令 GOTO…...

面试题-在ts中有两个类型,一个是a,一个是b,这两个联合起来就是c,如何实现联合

在 TypeScript 中&#xff0c;若要将两个类型 a 和 b 联合成一个新类型 c&#xff0c;可以使用 联合类型&#xff08;Union Type&#xff09; 或 交叉类型&#xff08;Intersection Type&#xff09;&#xff0c;具体取决于你的需求&#xff1a; 一、联合类型&#xff08;Unio…...

Mac电脑-触摸板增强工具-BetterTouchTool

BetterTouchTool mac 触摸板增强工具&#xff0c;允许用户使用各种手势来控制其计算机。 Bettertouchtool mac是一个小而高效的macOS应用程序&#xff0c;旨在帮助您为手势定义快捷方式。 此外&#xff0c;Bettertouchtool可用于使用常规鼠标和键盘快捷键&#xff0c;并提供伴…...

MySQL误删数据急救指南:基于Binlog日志的实战恢复详解

背景 数据误删是一个比较严重的场景 1.典型误操作场景 场景1&#xff1a;DELETE FROM orders WHERE status0 → 漏写AND create_time>‘2025-06-20’ 场景2&#xff1a;DROP TABLE customer → 误执行于生产环境 认识 binlog 1.binlog 的核心作用 记录所有 DDL/DML 操…...

API网关Apisix管理接口速查

&#x1f9ed; 管理接口总体分类&#xff08;基于 REST API&#xff09; 资源类别接口路径前缀功能说明路由&#xff08;Routes&#xff09;/apisix/admin/routes/{id}定义 HTTP 请求的匹配规则及转发目标服务&#xff08;Services&#xff09;/apisix/admin/services/{id}封装…...

React 组件通信

父传子 函数式组件 function Footer(props){const [count,setCount] useState(0)const {name,age} propsconst onClick ()>{setCount(count1)}return (<div><button onClick{()>{onClick()}}>点此1</button><div>{count}</div><di…...

Zephyr 电源管理机制深度解析:从 Tickless Idle 到平台 Suspend 实践

本文系统解析 Zephyr 的电源管理机制&#xff0c;包括 Tickless Idle 模式、系统 suspend/resume 生命周期管理、平台级功耗优化 Hook、自定义设备电源域&#xff0c;以及如何结合低功耗 SoC 实现最小化功耗设计。全文超过 5000 字&#xff0c;适合构建对功耗敏感的 IoT、BLE、…...

clickhouse-server连不上clickhouse-keeper的问题记录

背景 想简单部署一个1 shard 2 replica&#xff0c;1keeper的集群。 有两个虚拟机&#xff1a;192.168.1.3&#xff0c;192.168.1.6。 192.168.1.3&#xff1a;部署1个ck&#xff0c;1个keeper 192.168.1.6&#xff1a;部署1个ck 192.168.1.3和192.168.1.6的ck组成1个shar…...

Python 数据分析与可视化 Day 3 - Pandas 数据筛选与排序操作

&#x1f3af; 今日目标 掌握 DataFrame 的条件筛选&#xff08;布尔索引&#xff09;学会多条件筛选、逻辑运算熟练使用排序&#xff08;sort_values&#xff09;提升数据组织力结合列选择进行数据提取分析 &#x1f9ea; 一、列选择与基本筛选 ✅ 选择单列 / 多列 df[&quo…...

Android NDK下载链接及配置版本

Android NDK下载链接及配置版本 https://github.com/android/ndk/releases 在build.gralde里面这样配置ndk具体版本号&#xff1a; android {ndkVersion "27.0.12077973" } Android Studio报错&#xff1a;Could not move temporary workspace () to immutable locat…...

Mac Parallels Desktop Kali 2025 代理设置

Mac Parallels Desktop Kali 2025 代理设置 核心步骤&#xff1a; kali设置桥接wifi 查看kali和主机ip 运行命令ifconfig查看kali ip&#xff1a; mac主机ip&#xff1a; kali设置proxy ip填写主机ip&#xff0c;port为主机proxy端口 enjoy...

Python 的内置函数 hash

Python 内建函数列表 > Python 的内置函数 hash Python 的内置函数 hash() 是一个非常有用的工具函数&#xff0c;主要用于获取对象的哈希值。哈希值是一个固定长度的整数&#xff0c;代表该对象的唯一标识。在 Python 中&#xff0c;hash() 函数常用于字典键值、集合元素等…...

文生视频(Text-to-Video)

&#x1f552; 生成时间&#xff1a;每张图大概 10–60 秒&#xff08;取决于设备&#xff09; ✅ 二、文生视频&#xff08;Text-to-Video&#xff09; 以下项目中&#xff0c;很多都基于 SD 模型扩展&#xff0c;但视频生成复杂度高&#xff0c;生成时间一般 超过 30 秒&am…...

(LeetCode 面试经典 150 题) 80. 删除有序数组中的重复项 II (双指针、栈)

题目&#xff1a;80. 删除有序数组中的重复项 II 思路&#xff1a;左指针 left 类似于指向栈顶的下一个待填的元素&#xff0c;每次遍历只需看当前元素nums[i]和栈顶的下一个元素nums[left-2]是否相等&#xff0c;不等就可以插入栈当中。时间复杂度0(n)。 C版本&#xff1a; …...

【舞蹈】编排:如何对齐拍子并让小节倍数随BPM递减

音的强弱关系 当前划分编排最小单位的代码的分析 📊 代码逻辑分析 ✅ 完整性方面 代码逻辑相对完整,包含了: 结构段落分析(intro, verse, chorus等)强拍时间点提取歌词时间轴处理AI增强的编舞建议生成⚠️ 主要问题 1. 强拍对齐逻辑不够精确 # 当前代码只是简单提取…...

LangGraph--基础学习(工具调用)

本节将详细学习大模型是怎么调用工具的&#xff0c;为什么可以调用工具等等&#xff0c;手写一个工具调用&#xff0c;后续可以通过mcp自己调用即可&#xff0c;没必要自己写&#xff0c;但是学习过程中需要手写&#xff0c;通常怎么使用第三方工具调用呢&#xff1f; import o…...

华为云 Flexus+DeepSeek 实战:华为云单机部署 Dify-LLM 开发平台全流程指南【服务部署、模型配置、知识库构建全流程】

华为云 FlexusDeepSeek 实战&#xff1a;华为云单机部署 Dify-LLM 开发平台全流程指南【服务部署、模型配置、知识库构建全流程】 文章目录 华为云 FlexusDeepSeek 实战&#xff1a;华为云单机部署 Dify-LLM 开发平台全流程指南【服务部署、模型配置、知识库构建全流程】前言1、…...

【appium】2.初始连接脚本配置

连接配置 from appium import webdriver desired_caps {platformName: Android,automationName: UIAutomator2,deviceName: ZTEB880,appPackage: com.taobao.taobao,appActivity: com.taobao.tao.welcome.Welcome,noReset: True }driver webdriver.Remote(http://localhost:…...

磁性传感器在电机控制闭环系统中的反馈作用

磁性传感器的基本原理和类型 基本原理 &#xff1a;磁性传感器是基于磁学原理来检测磁场强度、方向或其他与磁场相关的物理量。常见的磁性传感器有霍尔传感器、磁阻传感器等。霍尔传感器是利用霍尔效应工作的&#xff0c;当电流通过置于磁场中的半导体材料时&#xff0c;在垂直…...

Python:.py文件如何变成双击可执行的windows程序?(版本1)

1、如下.py文件&#xff0c;右键重命名文件后缀名&#xff1a;py改为&#xff1a;pyw 2、修改时&#xff0c;提示如下&#xff1a;不用管点击&#xff1a;是即可 3、之后双击&#xff0c;即可执行python代码文件。 好的&#xff0c;我们来详细介绍一下 Python 的 .pyw 文件。 简…...

Spring Boot + MyBatis + Vue:全栈开发的深度剖析与实践指南

一、技术栈深度剖析 &#xff08;一&#xff09;Spring Boot&#xff1a;后端开发的加速器 Spring Boot 是基于 Spring 框架的一个开源 Java 项目&#xff0c;旨在简化基于 Spring 的应用开发。它通过自动配置机制&#xff0c;能够根据项目中添加的依赖自动配置 Spring 和相关…...

学习C++、QT---03(C++的输入输出、C++的基本数据类型介绍)

每日一言 你比想象中更强大&#xff0c;那些咬牙坚持的瞬间&#xff0c;都在雕刻更好的你。 案例&#xff1a;C的输入输出 但是我也会用c语言的方式来回顾c语言的写法&#xff0c;因为两种语言都是密不可分的&#xff0c;所以不能忘记&#xff0c;所以两个一起写 注意点&#…...

八、Redis的主从原理、哨兵

简介&#xff1a; 想要了解Redis的主从原理&#xff0c;首先得认识一个基本的分布式理论-CAP理论。要理解这个理论&#xff0c;其实也非常简单。 CAP理论 C&#xff1a;Consistency、A&#xff1a;Available、P&#xff1a;Partition tolerance 。这是CAP三个字母的全称。C&…...

springboot通过独立事务管理器实现资源隔离与精准控制​

安心流转站核心业务模块&#xff0c;为什么&#xff01;我们考虑这样设计&#xff0c;下面讲讲专用事务管理器的设计与必要性​&#xff01; 一、为什么需要专属事务管理器&#xff1f;​​ 在安心流转站模块中&#xff0c;存在 ​​「多资源混合操作」​​ 和 ​​「业务高敏…...

59-Oracle 10046事件-知识准备

上一篇说到了autotrace&#xff0c;SQL调试时候的获取性能和参数数据&#xff0c;直接用上trace&#xff0c;还有个更全能的工具10046。是不是很多小伙伴会对这么个数字&#xff0c;觉得起名很奇怪&#xff0c;数字起名任性。“10046”本质是Oracle内核事件的随机性技术编号&am…...

2025年渗透测试面试题总结-2025年HW(护网面试) 03(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 2025年HW(护网面试) 03 1. 同源策略&#xff08;Same-Origin Policy&#xff09; 2. XSS攻击用途 3. XSS类…...

嵌入式开发之嵌入式系统硬件架构设计时,如何选择合适的微处理器/微控制器?

在嵌入式系统硬件架构设计中&#xff0c;选择合适的微处理器 / 微控制器&#xff08;MCU/MPU&#xff09;是关键环节&#xff0c;需从多维度综合评估。以下是系统化的选择策略及核心考量因素&#xff1a; 一、明确应用需求与核心指标 1. 性能需求 处理能力&#xff1a;根据任…...

C++(面向对象编程——继承)

继承基础概念 1.什么是继承&#xff1f; 继承是C三大特性之一&#xff1b;继承是一个已经存在的类的基础上新建一个类&#xff0c;新建的类拥有已经存在的类的特性。主要提现的是代码复用的思想。新的类继承了基类的所有成员变量和成员函数&#xff0c;包括不显示的函数&…...

Unity Shader开发-着色器变体(2)-定义着色器变体

一.定义着色器变体 定义一个着色器变体&#xff08;Shader Variant&#xff09;从概念和实现上讲&#xff0c;主要包括以下几个核心部分 1.使用预编译指令来声明变体关键字 关键字是驱动变体生成的“开关”。它们是简单的字符串标识符&#xff0c;用于在 Shader 代码中标记不…...

Cookie和Session的作用和区别

Cookie 客户端持久化保存服务器数据的一种机制&#xff08;持久化存储就是存硬盘里&#xff09;。Cookie文件数据为键值对形式&#xff0c;客户端根据服务器域名的不同分别存储Cookie&#xff0c;不同域名的Cookie不同&#xff0c;不会产生冲突。 典型应用场景&#xff1a; 保…...

Redis集群部署终极指南:架构选型、生产部署与深度优化

第一部分&#xff1a;Redis集群技术全景解析 1.1 Redis集群演进史 单机时代&#xff08;2009-2012&#xff09;&#xff1a;Redis 2.8之前&#xff0c;纯单机模式复制时代&#xff08;2012-2015&#xff09;&#xff1a;Redis 2.8引入PSYNC改进复制哨兵时代&#xff08;2015-…...

腾讯云IM即时通讯:开启实时通信新时代

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;即时通讯已然成为互联网世界中不可或缺的关键元素。无论是个人日常生活中的社交互动&#xff0c;还是企业运营里的高效协作&#xff0c;即时通讯都发挥着举足轻重的作用&#xff0c;已然渗透到人们生活与工作的每一个角落…...

基于Qt的UDP主从服务器设计与实现

概述 一个基于Qt框架实现的UDP主从服务器系统&#xff0c;该系统具备自动主机选举、故障转移和状态同步等关键功能&#xff0c;适用于分布式能源管理系统中的设备通信与协调。 系统核心功能 1. 自动主机选举与故障转移 系统通过优先级机制实现自动主机选举&#xff0c;当主机…...

JVM(8)——详解分代收集算法

JVM 的分代收集算法不是一种具体的垃圾收集算法实现&#xff0c;而是一种指导思想和设计原则&#xff0c;是现代 JVM 垃圾收集器的基石。其核心思想源于对程序运行过程中对象生命周期分布的观察&#xff08;即弱分代假说&#xff09;。 核心思想与理论基础&#xff1a;分代假说…...

深入Java面试:从Spring Boot到微服务

深入Java面试&#xff1a;从Spring Boot到微服务 在准备互联网大厂的Java岗位面试时&#xff0c;掌握核心技术栈是关键。本文将从技术栈中选取几个重要的技术点进行探讨&#xff0c;帮助你在面试中脱颖而出。 问题一&#xff1a;Spring Boot的核心特性是什么&#xff1f; 面…...

【软考高级系统架构论文】论无服务器架构及其应用

论文真题 近年来,随着信息技术的迅猛发展和应用需求的快速更迭,传统的多层企业应用系统架构面临越来越多的挑战,已经难以适应这种变化。在这一背景下,无服务器架构(Serverless Architecture) 逐渐流行,它强调业务逻辑由事件触发,具有短暂的生命周期,运行于无状态的轻量…...

Snapchat矩阵运营新策略:亚矩阵云手机打造高效社交网络

1. Snapchat平台特性与风控挑战​​ Snapchat作为全球领先的即时社交平台&#xff0c;其独特的阅后即焚功能和强社交属性使其风控系统极为严格&#xff1a; ​​核心风控机制​​ ​​设备指纹检测​​&#xff1a;记录设备ID、系统版本、IP地址等硬件信息​​行为模式分析​…...

BGP路由反射器(RR)实验详解,结尾有详细脚本

目录 路由反射器基础概念 实验拓扑与设计 实验配置步骤 配置验证与排错 实验总结 完整配置命令集 路由反射器基础概念 在传统的IBGP网络中&#xff0c;为了防止路由环路&#xff0c;BGP规定通过IBGP学到的路由不能再传递给其他IBGP对等体&#xff0c;这导致所有IBGP路由…...

【JAVA】数组的使用

文章目录 前言一、数组的基本概念1.1 数组的创建和初始化1.2 数组的基本使用 二、数组是引用类型2.1 初始JVM的内存分布JVM内存划分&#xff08;按功能分区&#xff09; 2.2 基本类型变量与引用类型变量的区别2.3 再谈引用变量2.4 认识null 三、数组作为函数的参数和返回值四、…...

Python的6万张图像数据集CIFAR-10和CIFAR-100说明

CIFAR-10和CIFAR-100数据集是8000万张微小图像数据集的标记子集。CIFAR-10和CIFAR-100都是由AlexKrizhevsky、VinodNair和GeoffreyHinton创建。数据集说明的网页&#xff1a;https://www.cs.toronto.edu/~kriz/cifar.html 一、CIFAR-10数据集 &#xff08;一&#xff09;CIFA…...

CTF--PhP Web解题(走入CTF)

前情提要 分享有趣CTF题目&#xff0c;记录学习过程 题目&#xff08;带注释,方便理解&#xff09; <?php // 开启PHP源代码高亮显示&#xff0c;输出当前文件内容&#xff08;用于调试/展示&#xff09; highlight_file(__FILE__);// 关闭所有错误报告&#xff0c;防止敏感…...