服饰网站建设/如何使用免费b站推广网站

服饰网站建设,如何使用免费b站推广网站,推广互联网推广,网页制作的基本步骤和教程// 提交任务到线程池 template<class F, class... Args> auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {using return_type typename std::result_of<F(Args...)>…
// 提交任务到线程池
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {using return_type = typename std::result_of<F(Args...)>::type;auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));std::future<return_type> res = task->get_future();{std::unique_lock<std::mutex> lock(queue_mutex);if(stop) {throw std::runtime_error("enqueue on stopped ThreadPool");}tasks.emplace([task]() { (*task)(); });}condition.notify_one();return res;
}

基础知识点包括:右值、右值引用、完美转发、智能指针、Lambda表达式、可调用对象包装器function<void()>、绑定器bind、future、packaged_task


问题1:函数名(F&& f, Args&&… args) 这里的形参是啥意思?

ThreadPool::enqueue 这个函数的入参部分:

template <class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>

F&& f, Args&&... args 采用了一种 完美转发(Perfect Forwarding) 机制,主要是为了高效地传递参数,并保持参数的值类别(左值/右值)属性。

1. F&& f

  • 这里 F&& 是一个 通用引用(Universal Reference),用于接收任何类型的可调用对象(callable object),包括:
    • 函数指针
    • Lambda 表达式
    • std::function
    • 具有 operator() 的仿函数(functor)
    • 绑定的成员函数 std::bind
  • F&& 作为通用引用,不仅能接受左值(Lvalue),还能接受右值(Rvalue),并且能保留参数的原始类别。

2. Args&&... args

  • Args...可变模板参数(variadic template parameter),表示可以接受多个参数。
  • Args&&... 也是通用引用,它的作用是:
    • 可以接受任意数量的参数
    • 完美转发(Perfect Forwarding):如果 args 是左值,则保持左值;如果 args 是右值,则保持右值

3. 为什么要这样写?

主要是为了 提高泛型代码的效率,并 避免不必要的拷贝

如果直接使用:

template <class F, class... Args>
auto ThreadPool::enqueue(F f, Args... args) -> std::future<typename std::result_of<F(Args...)>::type>

那么:

  • F f 会导致可调用对象(如 lambda)被拷贝,而不是以原始形式传递。
  • Args... args 会导致所有参数都被拷贝或切片(slicing),而不是以正确的引用形式传递。

使用 F&& f, Args&&... args完美转发,可以:

  • 传递左值时,保留左值属性,避免不必要的拷贝。
  • 传递右值时,移动而不是拷贝,提高效率。
  • 允许传递任意类型的参数,使 enqueue 适用于各种可调用对象。

问题2:理解 std::result_of<F(Args…)>::type

std::result_of<F(Args...)>::type 是 C++11/14 中用于推导可调用对象 FArgs... 作为参数调用后的返回类型的工具。

然而,这个特性在 C++17 被弃用,在 C++20 被移除,并由 std::invoke_result_t<F, Args...> 取代。

1️⃣ 基本概念

std::result_of<F(Args...)>::type 解析 F 作为函数、函数指针、lambda 表达式或可调用对象,在传入 Args... 参数后,它的返回值是什么。

通俗解释

  • 如果 F(Args...) 是一个可调用表达式,那么 std::result_of<F(Args...)>::type 就是它的返回值类型。

问题3:make_shared<std::packaged_task<return_type()>>(std::bind(std::forward(f), std::forward(args)…))如何理解?

解析 std::make_sharedstd::packaged_task 中的用法

auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);

1. 代码结构拆解

  • std::make_shared<T>(...):
    • <> 里是 要创建的对象类型:这里是 std::packaged_task<return_type()>
    • () 里是 构造函数参数:这里是 std::bind(...) 的返回值

2. std::packaged_task 介绍

std::packaged_task 是 C++11 引入的 任务封装类,用于异步任务执行。它封装一个可调用对象(函数、lambda 表达式等),并允许获取其执行结果。

std::packaged_task<return_type()> task(func);
  • 作用:将 func 绑定到 task,稍后可以执行 task() 来调用 func,并通过 std::future 获取结果。
  • 适用场景
    • 异步任务执行(如 std::threadstd::async
    • 任务队列(线程池)

3. std::bind 介绍

std::bind 用于绑定函数和参数,返回一个可调用对象

std::bind(f, args...)
  • 作用:
    • 预绑定 f 的参数 args...
    • 返回一个可调用对象(类似 lambda
    • 适用于回调函数和延迟执行
示例
#include <iostream>
#include <functional>int add(int a, int b) { return a + b; }int main() {auto bound_func = std::bind(add, 10, 20);std::cout << bound_func() << std::endl; // 输出 30
}

4. 为什么要用 std::forward?

enqueue 函数中使用 std::forward 的主要目的是实现完美转发(Perfect Forwarding),确保参数 fargs... 能够保持它们原本的值类别(左值或右值),从而避免不必要的拷贝或移动,提高程序的性能。

详细解析:
1. F&& fArgs&&... args 万能引用(Universal References)
  • F&&Args&&... 并不是普通的右值引用,而是模板参数推导下的万能引用
  • fargs... 被传入时,它们可能是左值(lvalue)或者右值(rvalue)。
  • 如果直接传递这些参数,不加 std::forward,可能会导致不必要的拷贝或移动,影响性能。
2. 为什么需要 std::forward
  • std::bind(std::forward<F>(f), std::forward<Args>(args)...) 这部分代码中:

    • std::forward<F>(f) 确保 f 被正确地转发:
      • 如果 f 是左值,则 std::forward<F>(f) 也是左值。
      • 如果 f 是右值,则 std::forward<F>(f) 也是右值(即 std::move(f))。
    • std::forward<Args>(args)... 也是同理,保证每个参数 args... 都按照它原本的类别传递。
  • 如果不使用 std::forward

    • std::bind(f, args...) 会导致所有参数都被按值拷贝,或者被错误地转换为左值,可能会产生不必要的性能开销。

5. 代码运行流程

auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
执行步骤
  1. std::bind(std::forward<F>(f), std::forward<Args>(args)...)

    • 绑定 fargs...,返回一个可调用对象
  2. std::make_shared<std::packaged_task<return_type()>>(...)

    • 创建一个 std::packaged_task<return_type()>,用绑定的函数进行初始化
    • std::make_shared 进行 一次性分配内存(包括控制块+对象),提高效率
  3. 返回一个 std::shared_ptr<std::packaged_task<return_type()>>

    • task->operator()() 执行任务
    • task->get_future() 获取任务结果

问题4:如何理解tasks.emplace(task { (*task)(); })?

在这段代码中,tasks.emplace([task]() { (*task)(); }) 是一个关键操作,它将一个可调用对象(lambda 表达式)加入到 tasks 队列中。我们可以拆解它的逻辑来理解它的作用。

1. 理解 tasks

tasks 是一个任务队列,通常是 std::queue<std::function<void()>> 类型的变量,用于存储需要执行的任务。

std::queue<std::function<void()>> tasks;

因为 std::function<void()> 能够存储任意的可调用对象(如函数、lambda、函数对象等),所以我们可以把需要执行的任务封装进 std::function<void()>,然后存入队列。


2. 理解 task

auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);

共享智能指针shared_ptr 是一个模板类

// shared_ptr<T> 类模板中,提供了多种实用的构造函数, 语法格式如下:
std::shared_ptr<T> 智能指针名字(创建堆内存);

std::make_shared 是 C++11 标准引入的一个函数模板,用于创建 std::shared_ptr 对象。

这里的 task 是一个 std::shared_ptr<std::packaged_task<return_type()>>类型的对象,其中 std::packaged_task<return_type()> 封装了一个可调用对象(比如函数、lambda、成员函数等),当 (*task)() 被调用时,它会执行 f(args...),并将结果存入一个 std::future 供外部使用。


3. 为什么要使用智能指针?

在这段代码中,使用 智能指针 (shared_ptr) 主要是为了 管理任务的生命周期,具体来说,有以下几个关键原因:

1. 确保任务 (packaged_task) 的生命周期

addTask 函数中,任务 (packaged_task<returntype()>) 被封装到一个 shared_ptr 里:

auto task = make_shared<packaged_task<returntype()>> (bind(forward<F>(f), forward<Args>(args)...));

然后,它被存入任务队列:

queue_Tasksqueue.emplace([task]() {(*task)(); });

这里之所以使用 shared_ptr,主要是为了 确保 task 在队列中仍然有效,即:

  • task 可能会在 queue_Tasksqueue 中等待执行,而 addTask 可能已经执行完毕并返回。
  • 如果 task局部变量,那么在 addTask 结束时,它会被销毁,导致 悬空指针或未定义行为
  • shared_ptr 允许 task 在多个地方安全共享,即使 addTask 结束,task 仍然存在,直到任务真正执行完毕。

2. 避免 packaged_task 的拷贝

packaged_task 不允许拷贝,因为它内部维护了一个 future,拷贝可能导致 future 的所有权问题。
因此,shared_ptr 允许 在多个地方(任务队列和执行线程)安全传递 task,而无需拷贝。
所以,如果直接使用packaged_task<returntype()> task(bind(forward<F>(f), forward<Args>(args)...));是不行的,packaged_task类型的task不能拷贝,所以只能用智能指针来管理task。


3. 线程安全,避免悬空任务

任务队列中的任务可能会在不同线程中执行:

queue_Tasksqueue.emplace([task]() {(*task)(); });
  • 任务 task 可能会在 多个线程之间传递,如果没有 shared_ptr,就需要手动管理其生命周期,容易导致 内存泄漏或悬空指针
  • shared_ptr 让任务对象在最后一个线程执行完毕后才会被销毁,确保 线程安全

4. 简化资源管理,避免 new/delete

如果不使用 shared_ptr,可能需要手动 new 一个 packaged_task,然后在任务执行完后手动 delete,容易出错:

packaged_task<returntype()>* task = new packaged_task<returntype()>(bind(...));
queue_Tasksqueue.emplace([task]() {(*task)(); delete task; });

这样管理资源容易导致:

  • 内存泄漏(忘记 delete)。
  • 悬空指针delete 过早执行)。
  • 代码可读性降低

使用 shared_ptr 让 C++ 自动管理 packaged_task,避免手动 new/delete 的复杂性。


总结

使用 shared_ptr 管理 packaged_task 的生命周期,带来的好处有:

  1. 保证任务的生命周期:即使 addTask 结束,任务仍然有效,直到被执行完毕。
  2. 避免 packaged_task 的拷贝问题:确保 future 正确管理。
  3. 线程安全:任务队列中的任务可能在多个线程中共享,shared_ptr 确保不会提前销毁。
  4. 避免手动 new/delete,减少资源管理的复杂性,提高代码健壮性。

4. 为什么要用Lambda包装std::shared_ptr<std::packaged_task<returntype()>>类型的task?

1. 直接传入 task 会导致类型不匹配

线程池的任务队列 queue_Tasksqueue 是:

queue<function<void(void)>> queue_Tasksqueue;

即,任务队列存储的是 std::function<void()> 类型的可调用对象

task 的类型是:

std::shared_ptr<std::packaged_task<returntype()>>

问题:

  • shared_ptr<packaged_task<returntype()>> 不能隐式转换为 std::function<void()>
  • std::function<void()> 需要一个 可调用对象(比如函数、Lambda),但 shared_ptr 本身不是可调用对象

如果你尝试这样做:

queue_Tasksqueue.emplace(task);  // ❌ 编译错误

编译器会报错,因为 queue_Tasksqueue.emplace() 需要一个 std::function<void()>,但 shared_ptr<packaged_task<returntype()>> 不是一个可调用对象


2. 为什么用 lambda 可以解决问题?

我们可以用 Lambda task 变成一个可调用对象

queue_Tasksqueue.emplace([task]() { (*task)(); });

Lambda 的作用:

  1. 捕获 taskshared_ptr,保证 task 在任务队列中仍然有效,不会被提前销毁。
  2. 使 task 变成一个可调用对象,因为 (*task)(); 相当于 task->operator()(),执行 packaged_task 任务。

这样,Lambda 的类型就变成了 std::function<void()>,可以安全地存入 queue_Tasksqueue


3. 用 std::bind 也可以

除了 Lambda,你也可以用 std::bind

queue_Tasksqueue.emplace(std::bind(&std::packaged_task<returntype()>::operator(), task));

但 Lambda 更直观,而且 std::bind 可能在一些情况下会导致额外的拷贝,因此 Lambda 是最佳选择


4. 总结
写法是否可行原因
queue_Tasksqueue.emplace(task);错误shared_ptr<packaged_task> 不是可调用对象,不能隐式转换为 std::function<void()>
queue_Tasksqueue.emplace([task]() { (*task)(); });正确Lambda 让 task 变成可调用对象,并确保生命周期管理
queue_Tasksqueue.emplace(std::bind(&std::packaged_task<returntype()>::operator(), task));正确std::bind 也可以包装 task,但不如 Lambda 直观
核心结论
  • shared_ptr<packaged_task> 不是可调用对象,不能直接存入 std::function<void()>
  • Lambda 让 task 变成可调用对象,并确保其生命周期正确管理。
  • std::bind 也能解决问题,但 Lambda 更直观。

所以,使用:

queue_Tasksqueue.emplace([task]() { (*task)(); });

最安全、最直观的解决方案。🚀


ThreadPool::addTask(function<void()>) 这个函数中,不能使用 引用 (const function<void()>& task) 主要是因为 任务需要被存入队列并在稍后执行,而 std::function<void()> 可能会封装临时对象(如 Lambda)。然而,对于 packaged_task,情况有所不同。


5. 既然packaged_task 不能被拷贝,那可以用引用传递吗?

1. packaged_task 不能直接用引用

ThreadPool::addTask(F&& f, Args&&... args) 这个 模板方法 中:

template<typename F, typename... Args>
future<typename result_of<F(Args...)>::type> addTask(F&& f, Args&&... args)
{using returntype = typename result_of<F(Args...)>::type;// `packaged_task` 绑定函数和参数auto task = make_shared<packaged_task<returntype()>> (bind(forward<F>(f), forward<Args>(args)...));future<returntype> res = task->get_future();mutex_queuemutex.lock();queue_Tasksqueue.emplace([task]() { (*task)(); });mutex_queuemutex.unlock();cv_ConditionVariable.notify_one();return res;
}

在这里,我们使用了 shared_ptr<packaged_task<returntype()>>,为什么 不能用引用packaged_task<returntype()>&)呢?

(1) packaged_task 不能被拷贝

std::packaged_task 不能被拷贝,因为它内部包含 std::future,拷贝会导致 future 结果管理混乱。所以,如果尝试这样做:

queue_Tasksqueue.emplace(*task);  // 直接存入队列

会编译失败,因为 packaged_task 没有拷贝构造函数

(2) 不能直接使用引用

如果 task引用 (packaged_task<returntype()>&) 传递,而不是 shared_ptr

packaged_task<returntype()> task(bind(forward<F>(f), forward<Args>(args)...));
queue_Tasksqueue.emplace([&task]() { task(); });

那么:

  • task局部变量,在 addTask 结束时会被销毁。
  • 但任务队列 queue_Tasksqueue 里存储的 Lambda 可能会task 被销毁后才执行,导致 悬空引用,程序崩溃。
(3) 为什么 shared_ptr 可以解决问题
auto task = make_shared<packaged_task<returntype()>> (bind(forward<F>(f), forward<Args>(args)...));
queue_Tasksqueue.emplace([task]() { (*task)(); });

这里使用 shared_ptr 解决了问题:

  • shared_ptr 允许 task 在队列和执行线程之间共享生命周期
  • 只有当任务被执行完毕shared_ptr 计数归零时,packaged_task 才会被销毁,避免了悬空指针问题。

2. function<void()>packaged_task 的区别
类型可拷贝适合用 shared_ptr 吗?适合用引用吗?
std::function<void()>可以拷贝不需要,它本身就是拷贝管理不能用引用,可能封装临时对象
std::packaged_task<R()>不能拷贝需要 shared_ptr 管理生命周期不能用引用,可能被提前销毁

3. 总结
  • 为什么 std::function<void()> 不能用引用 (&task)?

    • 任务可能是 临时对象(如 Lambda)。
    • 任务需要 拷贝存入队列,引用会导致悬空引用。
  • 为什么 std::packaged_task<R()> 需要 shared_ptr

    • packaged_task 不能拷贝,但需要跨线程传递。
    • shared_ptr 确保 task 在队列里存活到执行,防止悬空指针。

因此,在 ThreadPool 里:

  • std::function<void()> 使用值传递,因为它可以安全拷贝。
  • std::packaged_task<R()> 使用 shared_ptr,避免生命周期管理问题。

直接使用引用 (&) 会导致线程安全问题或悬空指针,因此不适合在这里使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/72680.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python-leetcode 56.电话号码的字母组合

题目&#xff1a; 给定一个仅包含数字的2-9的字符串&#xff0c;返回所有它可能表示的字母组合&#xff0c;答案可以按任意顺序返回 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;&#xff0c;注意1不对应任何字母 方法一&#xff1a;深度优先搜索&#x…

5.0 VisionPro调用USB相机的方法与步骤说明(一)

本文介绍如何在C#中调用visionPro以处理USB相机采集到的图片。示例如下: 主要思路如下: 1. 使用AForge来打开以及采集usb相机照片。 usb相机处于一直运行状态。每隔100ms采集一次照片。且触发一次事件。 public void Start() { this.videoSourcePlayer.Stop(); …

论文阅读:Deep Hybrid Camera Deblurring for Smartphone Cameras

今天介绍一篇 ACM SIGGRAPH 2024 的文章&#xff0c;关于手机影像中的去模糊的文章。 Deep Hybrid Camera Deblurring for Smartphone Cameras Abstract 手机摄像头尽管取得了显著的进步&#xff0c;但由于传感器和镜头较为紧凑&#xff0c;在低光环境下的成像仍存在困难&am…

Linux中的基本指令(下)

目录 mv指令 more指令 less指令 head指令 tail 指令 继续理解文件 重定向和追加重定向操作 理解管道 find指令 whereis 指令 bc指令 uname ‒r指令 grep 指令 关机 扩展命令 zip/unzip 指令 tar指令 关于rzsz 系统间的文件互传 接上&#xff01; mv指令 m…

Unity WebGL IIS报错无法使用

Unity WebGL IIS报错无法使用 原因1&#xff1a;WebGL文件夹无访问权限 右键WebGL文件夹-属性 点击安全-编辑-添加 输入ever点击确定-应用即可

docker-compose install nginx(解决fastgpt跨区域)

CORS前言 CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种安全措施,它允许或拒绝来自不同源(协议、域名、端口任一不同即为不同源)的网页访问另一源中的资源。它的主要作用如下: 同源策略限制:Web 浏览器的同源策略限制了从一个源加载的文档或脚本如何与另一…

vulnhub靶场之loly靶机

前言 挑战攻克该靶机30分钟 靶机&#xff1a;loly靶机&#xff0c;IP地址为192.168.10.11 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.6 靶机和攻击机都采用VMware虚拟机&#xff0c;都采用桥接网卡模式 文章涉及的靶机及工具&#xff0c;都可以自行访问官网或者项…

Deepseek API+Python测试用例一键生成与导出-V1.0.2【实现需求文档图片识别与用例生成自动化】

在测试工作中&#xff0c;需求文档中的图片&#xff08;如界面设计图、流程图&#xff09;往往是测试用例生成的重要参考。然而&#xff0c;手动提取图片并识别内容不仅耗时&#xff0c;还容易出错。本文将通过一个自研小工具&#xff0c;结合 PaddleOCR 和大模型&#xff0c;自…

Excel(函数篇):COUNTIF与CONUTIFS函数、SUMIF与SUMIFS函数、ROUND函数、MATCH与INDEX函数、混合引用与条件格式

目录 COUNTIF和COUNTIFS函数COUNTIF函数COUNTIFS函数SUMIF和SUMIFS函数SUMIF函数SUMIFS函数SUMIFS函数与控件实现动态年月汇总ROUND、ROUNDUP、ROUNDDOWN函数单元格混合引用条件格式与公式,标记整行数据MATCH和INDEX函数COUNTIF和COUNTIFS函数 COUNTIF函数 统计下“苏州”出现…

上位机数据可视化:使用QtCharts绘制波形图

工程配置 CMake文件 find_package(Qt5 COMPONENTS Charts REQUIRED)target_link_libraries(zhd-desktop PRIVATE Qt5::Charts)包含头文件以及名称空间&#xff08;这个很重要&#xff0c;没有包含名称空间编译器会提示找不到相关的类型&#xff09; #include <QtCharts&g…

CSS - Pseudo-classes(伪类选择器)

目录 一、介绍二、常用种类三、案例实现案例一&#xff1a;a标签使用link/visited/hover/active案例二&#xff1a;表单元素使用focus/disabled案例三、通过其余伪类实现元素灵活选中 一、介绍 CSS 伪类&#xff08;Pseudo-classes&#xff09; 用于定义元素的特定状态或结构位…

Ubuntu22.04虚拟机里安装Yolov8流程

1. 安装pytorch sudo apt install nvidia-cuda-toolkit nvcc --version # 官方适配地址&#xff1a;https://download.pytorch.org/whl/torch/import torch print(torch.__version__) print(torch.cuda.is_available())2. 安装环境 # cuDNN 安装&#xff1a;https://develop…

Spring 声明式事务管理

Spring 编程的方式实现事务管理&#xff0c;这样太过麻烦&#xff0c;需要在每个方法上面加上相应的事务处理操作&#xff0c;声明式事务处理能够很好的解决这个问题&#xff0c;比如通过tx命名空间&#xff0c;这样只需要配置就可以检测到相关的方法&#xff0c;或者是通过tra…

Chat2DB:让数据库管理像聊天一样简单

数据库工具的痛点与破局 在数据爆炸的时代&#xff0c;数据库管理工具已成为企业高效运营的刚需。然而&#xff0c;传统工具如Navicat、DBeaver虽功能强大&#xff0c;却让非技术人员和SQL新手望而却步。复杂的界面、繁琐的手动操作、晦涩的语法规则&#xff0c;成为横亘在数据…

Navicat for Snowflake 震撼首发,激活数据仓库管理全新动能

近日&#xff0c;Navicat 家族迎来了一位全新成员 — Navicat for Snowflake。Snowflake 是一款基于云架构的现代数据仓库解决方案&#xff0c;以其弹性扩展、高性能和易用性著称。这次首发的Navicat for Snowflake 专为简化 Snowflake 数据库管理任务而精心打造。它凭借其直观…

【项目合集】智能语音小车-微信小程序控制

功能需求&#xff1a; 车子检测环境温度、湿度&#xff0c;上报 APP、WEB 端显示实时数据可通过 APP 控制小车前进、左转、右转可通过语音控制小车前进后退车上一个 LED 灯&#xff0c;可通过 WEB、小程序控制在 APP、WEB 上均可注册登录 硬件清单 硬件 功能 备注 ESP32 …

人工智能与人的智能,改变一生的思维模型分享【4】决策树

决策树&#xff08; DECISION TREE&#xff09; 一般由一个决策图和若干可能的结果组成。是一种通过罗列解题的关键步骤以及各步骤发生的条件和结果&#xff0c;由此来创建到达目标的规划。 我们很早就知道有一个方法&#xff0c;叫做当你苦闷、纠结的时候&#xff0c;把你的所…

利用余弦相似度在大量文章中找出抄袭的文章

我前面的2篇文章分别讲了如果利用余弦相似度来判断2篇文章的相似度&#xff0c;来确定文章是否存在抄袭&#xff0c;和余弦相似度的原理&#xff0c;即余弦相似度到底是怎么来判断文章的相似性高低的等等。这一篇再说下&#xff0c;对于文章字数多和大量文章时&#xff0c;如果…

设计模式-对象创建

对象创建 前言1. Factory Method1.1 模式介绍1.2 模式代码1.2.1 问题代码1.2.2 重构代码 1.3 模式类图1.4 要点总结 2. Abstract Factory2.1 模式介绍2.2 模式代码2.2.1 问题代码2.2.2 重构代码 2.3 模式类图2.4 要点总结 3. Prototype3.1 模式介绍3.2 模式代码3.3 模式类图3.4…

SQLAlchemy系列教程:批量插入数据

高效地批量插入数据对于应用程序的性能至关重要。SQLAlchemy为批处理操作提供了几种机制&#xff0c;可以最大限度地减少开销并加快数据库事务时间。在本指南中&#xff0c;我们将探讨如何使用SQLAlchemy执行批量插入&#xff0c;包括从基础技术到高级技术。 搭建环境 在开始之…