線程池介紹,分類,實現(工作原理,核心組成,拒絕策略),固態線程池的實現+詳細解釋(支持超時取消機制和不同的拒絕策略)

目錄

線程池

介紹

分類

實現

工作原理

核心組成

拒絕策略?

固態線程池

功能

std::future

實現

拒絕策略支持

提交任務

超時取消

用戶檢測取消

安全銷毀

代碼

測試


線程池

介紹

線程池(圖解,本質,模擬實現代碼),添加單例模式(懶漢思路+代碼)_線程池單例-CSDN博客

  • 包括線程池介紹+使用pthread庫接口

這里我們就使用c++11中的thread庫來實現一下,并且引入更多特性

分類

固定大小線程池(Fixed-size Thread Pool)

  • 線程池中的線程數在初始化時被設定并保持固定,不會根據負載自動擴展或收縮。

  • 適用于負載較為平穩的場景,例如Web服務器、數據庫連接池等。

動態線程池(DynamicThread Pool)

  • 線程池的線程數是動態變化的,根據任務的數量來增加或減少線程數。

  • 當線程池中沒有任務時,線程會被回收(通常有一個最大線程數限制)。

  • 適用于任務量不穩定、并發變化較大的場景,如文件處理、短時間的批量任務等。

單線程化線程池(Single-threaded Thread Pool)

  • 線程池中只有一個線程,所有任務都會按順序提交給這個線程執行。

  • 適用于串行化任務,如日志記錄、事件驅動任務等。

調度線程池(Scheduled Thread Pool)

  • 該線程池支持任務的延遲執行和周期性執行。

  • 通常用于定時任務或周期性任務,例如定時清理緩存等。

實現

工作原理

  • 線程池初始化:線程池初始化時創建一定數量的工作線程,并使其處于“等待”狀態,準備執行任務
  • 任務提交:當有新任務提交時,線程池將任務放入任務隊列中
  • 任務執行:線程池中的空閑線程從任務隊列中取出任務并執行
  • 任務完成:任務執行完后,線程回到等待狀態,準備接收新的任務
  • 線程銷毀:當線程池中的線程閑置超過一定時間,線程池可以銷毀一些線程以釋放系統資源(適用于動態線程池)

核心組成

一個線程池一般由以下幾個核心組件組成:

線程工作線程集合

  • 一組預先創建的固定數量或動態伸縮(包括核心線程和臨時創建)的線程
  • 每個線程循環從任務隊列中獲取任務執行

任務隊列

  • 用于存放等待執行的任務,一般為線程安全的隊列(如 std::queue + mutex),支持任務入隊/出隊

任務提交接口

  • 對外暴露的函數接口,用于將任務提交到線程池,如 submit()

同步機制

  • 用于保護共享資源和協調線程間關系(每個線程都要訪問任務隊列)
  • 常用 mutex, condition_variable, atomic

任務拒絕策略(Rejection Policy)

  • 當隊列已滿時,決定如何處理新任務(下面介紹)

生命周期管理(Shutdown & Destruction)

  • 控制線程池的啟動、停止、銷毀,確保線程安全退出并釋放資源

可選的任務取消機制

  • 支持任務在執行中被取消(比如因當前任務執行超時等原因)

可選的任務超時機制

  • 為每個任務設置執行超時,到期未完成的任務自動取消或通知中斷

拒絕策略?

拋出異常,拒絕執行(默認策略)

  • 拋出 RejectedExecutionException 異常,告知任務無法執行
  • 適合你希望及時發現問題并中止提交任務

由提交任務的線程執行

  • 將任務回退給調用者,即提交任務的線程執行任務,而不是交給線程池中的線程處理
  • 可以起到“削峰”作用,但影響主線程性能(會阻塞提交線程)

靜默丟棄

  • 丟棄無法執行的任務,不拋出異常
  • 適合對任務可丟棄、無嚴重后果的場景(如日志)

丟棄隊列中最舊的任務,然后嘗試重新提交當前任務

  • 適合任務有時效性(如更新 UI、股票報價)

固態線程池

功能

支持:

  • 固定線程數: 構造時指定線程數,固定數量線程常駐執行任務

  • 有界隊列: 支持設置任務隊列最大容量,避免過載

  • 拒絕策略支持: 支持 BLOCK(等待)、DISCARD(丟棄)、THROW(拋異常)三種策略

  • 提交任務: 提交 void(exec_context) 類型任務,通過適配器轉換為 void() 并存入任務隊列,最終返回 std::future<void>

  • 超時取消: 自動按任務類型 (因為我這里是把自己寫的搜索引擎項目中增設的線程池作為例子,所以這里的類型就分為建立索引,搜索,搜索聯想) , 設置超時時間,到時通知任務取消

  • 用戶檢測取消: 任務內部可用?canceled()?檢測取消請求并安全退出

  • 安全銷毀: 析構時安全停止所有線程并等待退出

  • 線程安全: 所有關鍵資源由 mutexcondition_variable 保護,支持多線程并發提交任務

std::future

std::future<T> 是 C++11 引入的標準庫類型

  • 可以異步獲取一個任務的執行結果
  • 作用 -- 當你把一個任務提交給線程池時,這個任務可能要等一會兒才能執行(畢竟線程有限,排隊中),那么你就需要一個東西來 “將來拿到結果”

實現

拒絕策略支持

定義了一個枚舉類

    enum class RejectionPolicy{BLOCK,DISCARD,THROW};

當任務提交后,根據當前的任務隊列使用情況和拒絕策略的設置,決定對任務的處理方式

{std::unique_lock<std::mutex> lock(mtx_);// 阻塞策略:等待有空間if (tasks_.size() >= max_queue_size_ && !stop_){if (reject_policy_ == RejectionPolicy::BLOCK){//因為這里是帶謂詞的wait,所以即使虛假喚醒也會在條件為真后返回cond_.wait(lock, [this]{ return tasks_.size() < max_queue_size_ || stop_; });}else if (reject_policy_ == RejectionPolicy::DISCARD){throw std::runtime_error("Task queue is full. Task was discarded.");}else if (reject_policy_ == RejectionPolicy::THROW){throw std::runtime_error("Task queue is full.");}}if (stop_){throw std::runtime_error("ThreadPool is stopping. Cannot accept new tasks.");}tasks_.emplace([taskWrapper](){ (*taskWrapper)(); });}

這里的靜默丟棄

  • 雖然定義上是不拋出異常,但為了調用方知道任務沒被接收,還是加上了

這里的while循環是為了防止虛假喚醒:

提交任務
    // 傳入void(exec_context) ,因為內部也需要配合template <class F>std::future<void> submit(const std::string &taskType, F &&f){auto timeout = getTimeoutForTask(taskType);auto cancellableTask = std::make_shared<CancellableTask>();cancellableTask->func = std::forward<F>(f);// 管理任務執行結果auto taskWrapper = std::make_shared<std::packaged_task<void()>>([cancellableTask](){ (*cancellableTask)(); });// 從packaged_task中獲取一個關聯的future對象std::future<void> taskFuture = taskWrapper->get_future();{std::unique_lock<std::mutex> lock(mtx_);// 阻塞策略:等待有空間if (tasks_.size() >= max_queue_size_ && !stop_){if (reject_policy_ == RejectionPolicy::BLOCK){cond_.wait(lock, [this]{ return tasks_.size() < max_queue_size_ || stop_; });}else if (reject_policy_ == RejectionPolicy::DISCARD){throw std::runtime_error("Task queue is full. Task was discarded.");}else if (reject_policy_ == RejectionPolicy::THROW){throw std::runtime_error("Task queue is full.");}}if (stop_){throw std::runtime_error("ThreadPool is stopping. Cannot accept new tasks.");}tasks_.emplace([taskWrapper](){ (*taskWrapper)(); });}cond_.notify_one();// 啟動一個后臺線程監控超時取消std::thread([taskFuture = std::move(taskFuture),controller = cancellableTask->controller, timeout]() mutable{if (taskFuture.wait_for(timeout) == std::future_status::timeout) {controller->notify_cancel();std::cerr << "[Timeout] Task exceeded time limit and was cancelled.\n";} }).detach();return taskFuture;}

這里因為任務隊列中使用的void()類型,而外部傳入的是void(exec_context) (因為需要內部配合停止)

  • 所以需要對類型進行一個轉換
  • 也就是通過cancellableTask 這個可取消的任務封裝器,實際是一個無參可調用對象,內部實例化一個exec_context ,然后調用帶參數版本的函數
  • 最后將 (*taskWrapper)() 放入隊列,實際就是調用了cancellableTask的operator()()
  • 于是實現了將void(exec_context) ->?void()
超時取消

首先,是定義了兩個模塊,分別的作用是 --? 取消狀態的設置 和 作為對外接口

// 控制標識符
struct exec_controller
{bool notify_cancel() { return _should_cancel.exchange(true); }bool should_cancel() const { return _should_cancel; }private:std::atomic<bool> _should_cancel{false};
};
// 判斷是否被取消
struct exec_context
{exec_context(std::shared_ptr<exec_controller> impl): _impl(std::move(impl)) {}bool canceled() const { return _impl->should_cancel(); }private:std::shared_ptr<exec_controller> _impl;
};
  • 使用原子變量避免競態條件
  • 讀取控制器可以被多個任務共享 (通過智能指針控制生命周期)

其次,定義了一個可取消的任務封裝器

struct CancellableTask
{// 封裝一個取消控制器std::shared_ptr<exec_controller> controller =std::make_shared<exec_controller>();// 將取消上下文封裝進普通任務中std::function<void(exec_context)> func;void operator()(){exec_context ctx{controller};func(ctx);}
};

然后,還定義了根據不同任務類型,返回對應超時時間的函數

 static std::chrono::millisecondsgetTimeoutForTask(const std::string &taskType){if (taskType == ns_helper::TASK_TYPE_BUILD_INDEX){return std::chrono::seconds(120);}else if (taskType == ns_helper::TASK_TYPE_PERSIST_INDEX){return std::chrono::seconds(4 * 3600);}else if (taskType == ns_helper::TASK_TYPE_SEARCH ||taskType == ns_helper::TASK_TYPE_AUTOCOMPLETE){return std::chrono::seconds(1);}else{return std::chrono::seconds(1);}}
  • 我這里因為是搜索引擎,并且沒有做性能優化,所以設置的時間比較長(跪)

并且,啟動了一個后臺線程去監控任務狀態

        // 啟動一個后臺線程監控超時取消std::thread([taskFuture = std::move(taskFuture),controller = cancellableTask->controller, timeout]() mutable{if (taskFuture.wait_for(timeout) == std::future_status::timeout) {controller->notify_cancel();std::cerr << "[Timeout] Task exceeded time limit and was cancelled.\n";} }).detach();
  • 這里就只講一下lambda內部邏輯,傳參會在下面的核心邏輯介紹
  • 總之,這個線程會等待任務完成timeout秒,如果未完成,則任務超時 -> 設置取消控制器的標識符
  • 這里還設置了調用了datach,讓它和主線程分離(也就是后臺運行)

最后,就是核心邏輯了

    // 傳入void(exec_context) ,因為內部也需要配合template <class F>std::future<void> submit(const std::string &taskType, F &&f){auto timeout = getTimeoutForTask(taskType);auto cancellableTask = std::make_shared<CancellableTask>();cancellableTask->func = std::forward<F>(f);// 管理任務執行結果auto taskWrapper = std::make_shared<std::packaged_task<void()>>([cancellableTask](){ (*cancellableTask)(); });// 從packaged_task中獲取一個關聯的future對象std::future<void> taskFuture = taskWrapper->get_future();{std::unique_lock<std::mutex> lock(mtx_);// 阻塞策略:等待有空間if (tasks_.size() >= max_queue_size_ && !stop_){if (reject_policy_ == RejectionPolicy::BLOCK){cond_.wait(lock, [this]{ return tasks_.size() < max_queue_size_ || stop_; });}else if (reject_policy_ == RejectionPolicy::DISCARD){throw std::runtime_error("Task queue is full. Task was discarded.");}else if (reject_policy_ == RejectionPolicy::THROW){throw std::runtime_error("Task queue is full.");}}if (stop_){throw std::runtime_error("ThreadPool is stopping. Cannot accept new tasks.");}tasks_.emplace([taskWrapper](){ (*taskWrapper)(); });}cond_.notify_one();// 啟動一個后臺線程監控超時取消std::thread([taskFuture = std::move(taskFuture),controller = cancellableTask->controller, timeout]() mutable{if (taskFuture.wait_for(timeout) == std::future_status::timeout) {controller->notify_cancel();std::cerr << "[Timeout] Task exceeded time limit and was cancelled.\n";} }).detach();return taskFuture;}
  • 其實大頭設計就是那些組件,把它們組合起來使用+任務代碼中顯式檢測取消標識,就能實現超時取消機制? -- 在后臺單獨啟動一個線程,監控提交的任務是否在規定的超時時間內完成,如果超時則通知取消
用戶檢測取消

傳入的任務也需要配合(檢測取消標識符,為真時結束執行)

void test_cancel_task(exec_context ctx)
{std::cout << "Task started.\n";for (int i = 0; i < 50; ++i){if (ctx.canceled()){std::cout << "Task detected cancellation, exiting early.\n";return;}std::this_thread::sleep_for(std::chrono::milliseconds(100));std::cout << "Task working... step " << i << "\n";}std::cout << "Task completed normally.\n";
}
安全銷毀
 explicit FixedThreadPool(size_t thread_count, size_t max_queue_size = 1000,RejectionPolicy policy = RejectionPolicy::BLOCK): max_queue_size_(max_queue_size), reject_policy_(policy), stop_(false){for (size_t i = 0; i < thread_count; ++i){workers_.emplace_back([this]{while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(mtx_);cond_.wait(lock, [this] { return stop_ || !tasks_.empty(); });if (stop_ && tasks_.empty())return;task = std::move(tasks_.front());tasks_.pop();}task();} });}}
~FixedThreadPool(){{std::unique_lock<std::mutex> lock(mtx_);stop_ = true;}cond_.notify_all();for (std::thread &worker : workers_){if (worker.joinable()){worker.join();}}}
  • 釋放資源前,先設置停止標志,并喚醒所有線程,讓線程檢測到停止標識后,安全地將隊列中的遺留任務處理完畢,然后釋放線程資源

代碼

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <exception>
#include <functional>
#include <future>
#include <iostream>
#include <mutex>
#include <optional>
#include <queue>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>#include "../code/assistance.hpp"// --- exec control logic ---struct exec_controller
{bool notify_cancel() { return _should_cancel.exchange(true); }bool should_cancel() const { return _should_cancel; }private:std::atomic<bool> _should_cancel{false};
};struct exec_context
{exec_context(std::shared_ptr<exec_controller> impl): _impl(std::move(impl)) {}bool canceled() const { return _impl->should_cancel(); }private:std::shared_ptr<exec_controller> _impl;
};// --- CancellableTask ---struct CancellableTask
{std::shared_ptr<exec_controller> controller =std::make_shared<exec_controller>();std::function<void(exec_context)> func;void operator()(){exec_context ctx{controller};func(ctx);}
};// --- FixedThreadPool ---class FixedThreadPool
{
public:enum class RejectionPolicy{BLOCK,DISCARD,THROW};explicit FixedThreadPool(size_t thread_count, size_t max_queue_size = 1000,RejectionPolicy policy = RejectionPolicy::BLOCK): max_queue_size_(max_queue_size), reject_policy_(policy), stop_(false){for (size_t i = 0; i < thread_count; ++i){workers_.emplace_back([this]{while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(mtx_);cond_.wait(lock, [this] { return stop_ || !tasks_.empty(); });if (stop_ && tasks_.empty())return;task = std::move(tasks_.front());tasks_.pop();}task();} });}}~FixedThreadPool(){{std::unique_lock<std::mutex> lock(mtx_);stop_ = true;}cond_.notify_all();for (std::thread &worker : workers_){if (worker.joinable()){worker.join();}}}template <class F>std::future<void> submit(const std::string &taskType, F &&f){auto timeout = getTimeoutForTask(taskType);auto cancellableTask = std::make_shared<CancellableTask>();cancellableTask->func = std::forward<F>(f);auto taskWrapper = std::make_shared<std::packaged_task<void()>>([cancellableTask](){ (*cancellableTask)(); });std::future<void> taskFuture = taskWrapper->get_future();{std::unique_lock<std::mutex> lock(mtx_);// 阻塞策略:等待有空間if (reject_policy_ == RejectionPolicy::BLOCK){cond_.wait(lock,[this]{ return tasks_.size() < max_queue_size_ || stop_; });}// 丟棄策略:拋出異常(不再返回默認構造的 future)else if (reject_policy_ == RejectionPolicy::DISCARD){if (tasks_.size() >= max_queue_size_){throw std::runtime_error("Task queue is full. Task was discarded.");}}// 異常策略:同樣拋出異常else if (reject_policy_ == RejectionPolicy::THROW){if (tasks_.size() >= max_queue_size_){throw std::runtime_error("Task queue is full.");}}if (stop_){throw std::runtime_error("ThreadPool is stopping. Cannot accept new tasks.");}tasks_.emplace([taskWrapper](){ (*taskWrapper)(); });}cond_.notify_one();// 啟動一個后臺線程監控超時取消std::thread([taskFuture = std::move(taskFuture),controller = cancellableTask->controller, timeout]() mutable{if (taskFuture.wait_for(timeout) == std::future_status::timeout) {controller->notify_cancel();std::cerr << "[Timeout] Task exceeded time limit and was cancelled.\n";} }).detach();return taskFuture;}private:static std::chrono::millisecondsgetTimeoutForTask(const std::string &taskType){if (taskType == ns_helper::TASK_TYPE_BUILD_INDEX){return std::chrono::seconds(120);}else if (taskType == ns_helper::TASK_TYPE_PERSIST_INDEX) // ? 修復這里{return std::chrono::seconds(4 * 3600);}else if (taskType == ns_helper::TASK_TYPE_SEARCH ||taskType == ns_helper::TASK_TYPE_AUTOCOMPLETE){return std::chrono::seconds(1);}else{return std::chrono::seconds(1);}}std::vector<std::thread> workers_;std::queue<std::function<void()>> tasks_;std::mutex mtx_;std::condition_variable cond_;bool stop_;size_t max_queue_size_;RejectionPolicy reject_policy_;
};
測試
void test_cancel_task(exec_context ctx)
{std::cout << "Task started.\n";for (int i = 0; i < 50; ++i){if (ctx.canceled()){std::cout << "Task detected cancellation, exiting early.\n";return;}std::this_thread::sleep_for(std::chrono::milliseconds(100));std::cout << "Task working... step " << i << "\n";}std::cout << "Task completed normally.\n";
}int main()
{FixedThreadPool fp(2, 10, FixedThreadPool::RejectionPolicy::BLOCK);auto future = fp.submit("search", test_cancel_task);try{if (future.valid()){future.get();}}catch (const std::exception &e){std::cout << "Task threw exception: " << e.what() << "\n";}return 0;
}

動態線程池會在另一篇講(我還沒寫 1 - 1) ,大家也可以幫我糾一糾錯(跪倒)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/84120.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84120.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/84120.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

紡線機與PLC通訊故障?ETHERCAT/CANopen網關秒解協議難題

在紡織行業智能化轉型浪潮中&#xff0c;設備間高效通信是實現自動化生產的關鍵。JH-ECT009疆鴻智能EtherCAT轉CANopen協議轉換網關&#xff0c;憑借出色的協議適配能力&#xff0c;成功架起倍福PLC與自動紡線機間的通信橋梁&#xff0c;為紡織廠自動化生產注入強勁動力。 紡織…

深度剖析并發I/O模型select、poll、epoll與IOCP核心機制

核心概要&#xff1a;select、poll、epoll 和 IOCP 是四種用于提升服務器并發處理能力的I/O模型或機制。前三者主要屬于I/O多路復用范疇&#xff0c;允許單個進程或線程監視多個I/O流的狀態&#xff1b;而 IOCP 則是一種更為徹底的異步I/O模型。 一、引言&#xff1a;為何需要這…

microsoft中word如何添加個人簽名

https://support.microsoft.com/zh-cn/office/%E6%8F%92%E5%85%A5%E7%AD%BE%E5%90%8D-f3b3f74c-2355-4d53-be89-ae9c50022730 插入簽名圖片 圖片格式選擇裁剪合適的大小 使用的簽名如果不是白色紙張的話可以重新著色 依次點擊圖片格式——顏色——重新著色——黑白50% 設置透…

linux初識--基礎指令

Linux下基礎指令 ls 指令 語法&#xff1a; ls [ 選項 ] [ ?錄或?件 ] 功能&#xff1a;對于?錄&#xff0c;該命令列出該?錄下的所有??錄與?件。對于?件&#xff0c;將列出?件名以及其他信 息。 常?選項&#xff1a; -a 列出?錄下的所有?件&#xff0c;包括以…

實戰:Dify智能體+Java=自動化運營工具!

我們在運營某個圈子的時候&#xff0c;可能每天都要將這個圈子的“熱門新聞”發送到朋友圈或聊天群里&#xff0c;但依靠傳統的實現手段非常耗時耗力&#xff0c;我們通常要先收集熱門新聞&#xff0c;再組裝要新聞內容&#xff0c;再根據內容設計海報等。 那怎么才能簡化并高…

RabbitMQ可靠傳輸——持久性、發送方確認

一、持久性 前面學習消息確認機制時&#xff0c;是為了保證Broker到消費者直接的可靠傳輸的&#xff0c;但是如果是Broker出現問題&#xff08;如停止服務&#xff09;&#xff0c;如何保證消息可靠性&#xff1f;對此&#xff0c;RabbitMQ提供了持久化功能&#xff1a; 持久…

(Java基礎筆記vlog)Java中常見的幾種設計模式詳解

前言&#xff1a; 在 Java 編程里&#xff0c;設計模式是被反復使用、多數人知曉、經過分類編目的代碼設計經驗總結。他能幫助開發者更高效地解決常見問題&#xff0c;提升代碼的可維護性、可擴展性和復用性。下面介紹Java 中幾種常見的設計模式。 單例模式&#xff08;Singlet…

(8)Spring Boot 原生鏡像支持

Spring Boot 原生鏡像支持 ?? 點擊展開題目 在Spring Boot 3.x中,如何設計一個支持GraalVM原生鏡像的微服務?需要特別注意哪些限制? ?? Spring Boot 3.x 原生鏡像概述 Spring Boot 3.x 通過 Spring Native 項目提供了對 GraalVM 原生鏡像的一流支持,使開發者能夠將 S…

不使用SOAP,從PDF表單連接數據庫

不使用SOAP協議&#xff0c;通過XFDF格式實現PDF表單與數據庫交互的方法。該方法兼容免費的Adobe Reader&#xff0c;且無需特殊權限設置。 背景與問題 歷史方案: Adobe曾提供ADBC接口&#xff08;基于ODBC&#xff09;&#xff0c;但在Acrobat 9后被移除。SOAP方案在免費版Rea…

HTTP由淺入深

文章目錄 概述特點URL HTTP 與 HTTPS概述HTTP 工作原理HTTPS 的作用區別總結 請求報文請求行常見請求方法請求頭請求體Content-Type 詳解常見場景 Content-Type 對應關系 響應報文響應行狀態碼詳解1xx&#xff1a;信息響應&#xff08;Informational&#xff09;2xx&#xff1a…

Redis淘汰策略

Redis有八種淘汰策略 noeviction &#xff1a;不進行淘汰&#xff0c;直接報錯。allkeys-lru &#xff1a;隨機淘汰最久未使用的鍵。volatile-lru &#xff1a;從設置了過期時間的鍵中&#xff0c;隨機淘汰最久未使用的鍵。allkeys-random &#xff1a;隨機淘汰某個鍵。volati…

Maven打包SpringBoot項目,因包含SpringBootTest單元測試和Java預覽版特性導致打包失敗

SpringBoot啟用Java預覽版特性&#xff08;無測試類&#xff09; 在pom.xml文件中加入以下配置表示啟用Java預覽版 <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration>…

Makefile快速入門

簡介?&#xff1a; ? Makefile? 是一種用于自動化構建和管理軟件項目的工具文件&#xff0c;通常與 make 命令配合使用。它通過定義?規則?&#xff08;rules&#xff09;來指定如何從源文件生成目標文件&#xff08;如可執行程序或庫&#xff09;&#xff0c;并自動…

RISC-V 開發板 MUSE Pi Pro OpenCV結合Gstreamer實時顯示CSI攝像頭

視頻講解&#xff1a;RISC-V 開發板 MUSE Pi Pro OpenCV結合Gstreamer實時顯示CSI攝像頭_嗶哩嗶哩_bilibili RISC-V 開發板 MUSE Pi Pro OpenCV結合Gstreamer實時顯示CSI攝像頭 安裝opencv相關庫 sudo apt install libopencv-dev python3 python3-opencv 測試使用的CSI攝像頭…

如何用JAVA手寫一個Tomcat

一、初步理解Tomcat Tomcat是什么&#xff1f; Tomcat 是一個開源的 輕量級 Java Web 應用服務器&#xff0c;核心功能是 運行 Servlet/JSP。 Tomcat的核心功能&#xff1f; Servlet 容器&#xff1a;負責加載、實例化、調用和銷毀 Servlet。 HTTP 服務器&#xff1a;監聽端口…

短劇系統開發與抖音生態融合:短視頻時代的新風口與商業機遇

在短視頻內容井噴的時代&#xff0c;“短劇”作為一種新興內容形態&#xff0c;正以驚人的速度搶占用戶注意力。抖音、快手等平臺日均播放量破億的短劇作品&#xff0c;不僅催生了新的內容創作風口&#xff0c;更推動了短劇系統開發的巨大市場需求。本文將深度解析短劇系統開發…

《云原生安全攻防》-- K8s日志審計:從攻擊溯源到安全實時告警

當K8s集群遭受入侵時&#xff0c;安全管理員可以通過審計日志進行攻擊溯源&#xff0c;通過分析攻擊痕跡&#xff0c;我們可以找到攻擊者的入侵行為&#xff0c;還原攻擊者的攻擊路徑&#xff0c;以便修復安全問題。 在本節課程中&#xff0c;我們將介紹如何配置K8s審計日志&am…

3dczml時間動態圖型場景

在cesium中我們了可以使用czml數據來生成可以隨時間變化而變化的物體. 首先導入czml數據 設置時間范圍 id: "point" //物體在什么時間范圍可用 availability:"2012-08-04T16:00:00Z/2012-08-04T16:05:00Z"position:{ //設置物體的起始時間 epoch:"…

超小多模態視覺語言模型MiniMind-V 訓練

簡述 MiniMind-V 是一個超適合初學者的項目&#xff0c;讓你用普通電腦就能訓一個能看圖說話的 AI。訓練過程就像教小孩&#xff1a;先準備好圖文材料&#xff08;數據集&#xff09;&#xff0c;教它基礎知識&#xff08;預訓練&#xff09;&#xff0c;再教具體技能&#xf…

01-jenkins學習之旅-window-下載-安裝-安裝后設置向導

1 jenkins簡介 百度百科介紹&#xff1a;Jenkins是一個開源軟件項目&#xff0c;是基于Java開發的一種持續集成工具&#xff0c;用于監控持續重復的工作&#xff0c;旨在提供一個開放易用的軟件平臺&#xff0c;使軟件項目可以進行持續集成。 [1] Jenkins官網地址 翻譯&…