C++線程池執行步驟分析,總結線程池流程

線程池流程總結:

1、構造函數中創建線程,并添加到線程池(構造函數返回時,線程自動啟動,并停在等待wait:從線程池取出一個任務處);
2、主線程中添加任務,到任務隊列。并用“條件變量”通知一個線程,從線程池取出一個任務;
3、取出任務后,執行線程的任務函數 =》回調添加的“實際的線程函數”;
4、主線程執行完,return返回 =》調用線程池析構函數;
5、“條件變量”通知所有線程停止,使得線程循環退出,并等待所有線程完成任務;
6、主線程main結束。

一、C++線程池1

1、用c++封裝線程池

#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>using namespace std;class ThreadPool {
public:ThreadPool(size_t threads) : stop(false) {for (size_t i = 0; i < threads; ++i) {//一、lambda表達式返回值是一個線程對象,為什么沒有看見創建線程的語句?// thread是什么時候創建的呢?// //二、lambda表達式什么時候執行?// 1.當線程池的構造函數返回時,線程池中的線程才開始運行// 2.當你創建一個std::thread對象并傳入一個函數時(對象的實例化),// 這個線程會自動開始執行該函數。因此,通常你不需要顯式調用start()方法!//lambda表達式創建線程,并將線程加入線程池workers.emplace_back([this] {//線程循環,不斷從任務隊列中取出任務并執行while (true) {//取出任務std::function<void()> task;{//互斥鎖保護:任務隊列和線程池停止狀態std::unique_lock<std::mutex> lock(this->queueMutex);bool empty = this->tasks.empty();bool stopped = this->stop;//等待條件變量通知或線程池停止this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });//線程池停止且任務隊列為空,退出線程循環if (this->stop && this->tasks.empty()) return;//取出任務task = std::move(this->tasks.front());//從任務隊列中刪除任務this->tasks.pop();}//執行任務task();}});}}//定義任務入隊函數模板template<class F, class... Args>void enqueue(F&& fun, Args&&... args) // 添加任務到任務隊列(傳遞:線程函數、參數){//將任務封裝成std::functionauto task = std::bind(std::forward<F>(fun), std::forward<Args>(args)...);{//互斥鎖保護:任務隊列和線程池停止狀態std::unique_lock<std::mutex> lock(queueMutex);//線程池停止if (stop) throw std::runtime_error("Enqueue on stopped ThreadPool");//將任務加入任務隊列tasks.emplace(task);}//通知一個線程condition.notify_one();}// 析構函數:等待所有線程完成任務,并停止線程池~ThreadPool() {{//互斥鎖保護:線程池停止狀態std::unique_lock<std::mutex> lock(queueMutex);stop = true;}//通知所有線程condition.notify_all();//等待所有線程完成任務for (std::thread& worker : workers) {worker.join();}}
private:std::vector<std::thread> workers;//線程池std::queue<std::function<void()>> tasks;//任務隊列std::mutex queueMutex;//互斥鎖(保護任務隊列tasks 和線程池停止狀態stop)std::condition_variable condition;//條件變量(加入任務到任務隊列時通知一個線程)bool stop;//線程池是否停止
};

2、main測試

int main() {ThreadPool pool(2); // 創建一個包含4個線程的線程池for (int i = 0; i < 2; ++i) { // 添加2個任務到線程池中執行//任務入隊函數模板:輸出任務編號和線程IDpool.enqueue([i] { std::cout << "Task " << i << " is executed by thread " << std::this_thread::get_id() << std::endl; });}system("pause");return 0; // 主線程等待所有工作線程完成(在析構函數中處理)
}

3、通過測試,可以看出“線程池執行步驟”:

1、主線程 ThreadPool pool(2); // 創建一個包含4個線程的線程池
ThreadPool構造函數中:向“線程池workers”中添加lambda表達式形式的線程
//1.1線程循環,不斷從任務隊列中取出任務并執行
while(true)
{//1.2等待:條件變量通知或線程池停止this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
}2、主線程中添加任務,到任務隊列 函數模板:輸出任務編號和線程ID
pool.enqueue([i] {?std::cout << "Task " << i << " is executed by thread " << std::this_thread::get_id() << std::endl;?});//2.1將任務加入任務隊列(先用包裝器function包裝任務)
tasks.emplace(task);//2.2條件變量通知一個線程(每加入一個任務,就通知一次線程執行任務!!)
condition.notify_one();//2.3線程循環中的等待(條件變量通知),滿足條件,開始向下執行!
this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });//2.4從任務隊列取出任務
task = std::move(this->tasks.front());//2.5 執行任務(回調)task();=》回調“實際的線程函數”=》就是添加進來的lambda表達式:std::cout << "Task " << i << " is executed by thread " << std::this_thread::get_id() << std::endl;?3、主線程執行完。return 0; // 主線程返回//3.1 調用線程池析構函數
~ThreadPool()?
{//1.設置線程停止標識為truestop = true;//2.通知所有線程condition.notify_all();=》//線程循環while(true){//while循環一直在這等待!(lock滿足條件,向下執行!)this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });//當線程任務執行完成:stop = true;//線程池停止且任務隊列為空,退出線程循環if (this->stop && this->tasks.empty()) return;}3.//等待所有線程完成任務worker.join();
}4、主線程結束

二、C++線程池2

1、線程池.h頭文件

#ifndef _thread_pool_HPP
#define _thread_pool_HPP#include <vector>
#include <deque>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>//!
//! convenience macro to log with file and line information
//!
#ifdef __SOLA_LOGGING_ENABLED
#define __SOLA_LOG(level, msg) sola::level(msg, __FILE__, __LINE__);
#else
#define __SOLA_LOG(level, msg)
#endif /* __SOLA_LOGGING_ENABLED */namespace sola {class logger_iface {
public://! ctorlogger_iface(void) = default;//! dtorvirtual ~logger_iface(void) = default;//! copy ctorlogger_iface(const logger_iface&) = default;//! assignment operatorlogger_iface& operator=(const logger_iface&) = default;public://!//! debug logging//!//! \param msg message to be logged//! \param file file from which the message is coming//! \param line line in the file of the message//!virtual void debug(const std::string& msg, const std::string& file, std::size_t line) = 0;//!//! info logging//!//! \param msg message to be logged//! \param file file from which the message is coming//! \param line line in the file of the message//!virtual void info(const std::string& msg, const std::string& file, std::size_t line) = 0;//!//! warn logging//!//! \param msg message to be logged//! \param file file from which the message is coming//! \param line line in the file of the message//!virtual void warn(const std::string& msg, const std::string& file, std::size_t line) = 0;//!//! error logging//!//! \param msg message to be logged//! \param file file from which the message is coming//! \param line line in the file of the message//!virtual void error(const std::string& msg, const std::string& file, std::size_t line) = 0;
};//!
//! default logger class provided by the library
//!
class logger : public logger_iface {
public://!//! log level//!enum class log_level {error = 0,warn  = 1,info  = 2,debug = 3};public://! ctorlogger(log_level level = log_level::info);//! dtor~logger(void) = default;//! copy ctorlogger(const logger&) = default;//! assignment operatorlogger& operator=(const logger&) = default;public://!//! debug logging//!//! \param msg message to be logged//! \param file file from which the message is coming//! \param line line in the file of the message//!void debug(const std::string& msg, const std::string& file, std::size_t line);//!//! info logging//!//! \param msg message to be logged//! \param file file from which the message is coming//! \param line line in the file of the message//!void info(const std::string& msg, const std::string& file, std::size_t line);//!//! warn logging//!//! \param msg message to be logged//! \param file file from which the message is coming//! \param line line in the file of the message//!void warn(const std::string& msg, const std::string& file, std::size_t line);//!//! error logging//!//! \param msg message to be logged//! \param file file from which the message is coming//! \param line line in the file of the message//!void error(const std::string& msg, const std::string& file, std::size_t line);private://!//! current log level in use//!log_level m_level;//!//! mutex used to serialize logs in multithreaded environment//!std::mutex m_mutex;
};//!
//! variable containing the current logger
//! by default, not set (no logs)
//!
extern std::unique_ptr<logger_iface> active_logger;//!
//! debug logging
//! convenience function used internally to call the logger
//!
//! \param msg message to be logged
//! \param file file from which the message is coming
//! \param line line in the file of the message
//!
void debug(const std::string& msg, const std::string& file, std::size_t line);//!
//! info logging
//! convenience function used internally to call the logger
//!
//! \param msg message to be logged
//! \param file file from which the message is coming
//! \param line line in the file of the message
//!
void info(const std::string& msg, const std::string& file, std::size_t line);//!
//! warn logging
//! convenience function used internally to call the logger
//!
//! \param msg message to be logged
//! \param file file from which the message is coming
//! \param line line in the file of the message
//!
void warn(const std::string& msg, const std::string& file, std::size_t line);//!
//! error logging
//! convenience function used internally to call the logger
//!
//! \param msg message to be logged
//! \param file file from which the message is coming
//! \param line line in the file of the message
//!
void error(const std::string& msg, const std::string& file, std::size_t line);class thread_pool{public://任務包裝器typedef std::function<void()> task_t;thread_pool(int init_size = 3);~thread_pool();void stop();void add_task(const task_t&);  //thread safe; 添加任務private:thread_pool(const thread_pool&);//禁止復制拷貝.const thread_pool& operator=(const thread_pool&);bool is_started() { return m_is_started; }void start();//啟動線程池void thread_loop();//線程循環函數task_t take();//取任務函數private:typedef std::vector<std::thread*> threads_t;//線程容器typedef std::deque<task_t> tasks_t;//任務隊列int m_init_threads_size;//初始線程數量threads_t m_threads;//線程容器tasks_t m_tasks;//任務隊列std::mutex m_mutex;//互斥鎖std::condition_variable m_cond;//條件變量bool m_is_started;//線程池是否啟動};}
#endif

2、線程池cpp文件

#include <assert.h>
#include <iostream>
#include <sstream>
#include "thread_pool.hpp"namespace sola {std::unique_ptr<logger_iface> active_logger = nullptr;static const char black[]  = {0x1b, '[', '1', ';', '3', '0', 'm', 0};static const char red[]    = {0x1b, '[', '1', ';', '3', '1', 'm', 0};static const char yellow[] = {0x1b, '[', '1', ';', '3', '3', 'm', 0};static const char blue[]   = {0x1b, '[', '1', ';', '3', '4', 'm', 0};static const char normal[] = {0x1b, '[', '0', ';', '3', '9', 'm', 0};logger::logger(log_level level): m_level(level) {}voidlogger::debug(const std::string& msg, const std::string& file, std::size_t line) {if (m_level >= log_level::debug) {std::lock_guard<std::mutex> lock(m_mutex);std::cout << "[" << black << "DEBUG" << normal << "][sola::logger][" << file << ":" << line << "] " << msg << std::endl;}}voidlogger::info(const std::string& msg, const std::string& file, std::size_t line) {if (m_level >= log_level::info) {std::lock_guard<std::mutex> lock(m_mutex);std::cout << "[" << blue << "INFO " << normal << "][sola::logger][" << file << ":" << line << "] " << msg << std::endl;}}voidlogger::warn(const std::string& msg, const std::string& file, std::size_t line) {if (m_level >= log_level::warn) {std::lock_guard<std::mutex> lock(m_mutex);std::cout << "[" << yellow << "WARN " << normal << "][sola::logger][" << file << ":" << line << "] " << msg << std::endl;}}voidlogger::error(const std::string& msg, const std::string& file, std::size_t line) {if (m_level >= log_level::error) {std::lock_guard<std::mutex> lock(m_mutex);std::cerr << "[" << red << "ERROR" << normal << "][sola::logger][" << file << ":" << line << "] " << msg << std::endl;}}voiddebug(const std::string& msg, const std::string& file, std::size_t line) {if (active_logger)active_logger->debug(msg, file, line);}voidinfo(const std::string& msg, const std::string& file, std::size_t line) {if (active_logger)active_logger->info(msg, file, line);}voidwarn(const std::string& msg, const std::string& file, std::size_t line) {if (active_logger)active_logger->warn(msg, file, line);}voiderror(const std::string& msg, const std::string& file, std::size_t line) {if (active_logger)active_logger->error(msg, file, line);}static std::stringget_tid(){std::stringstream tmp;tmp << std::this_thread::get_id();return tmp.str();}//線程池構造函數thread_pool::thread_pool(int init_size):m_init_threads_size(init_size),m_mutex(),m_cond(),m_is_started(false){//構造函數中自動啟動線程池start();}thread_pool::~thread_pool(){//如果線程池已經啟動,則先停止if(m_is_started){stop();}}//啟動線程池void thread_pool::start(){assert(m_threads.empty());m_is_started = true;m_threads.reserve(m_init_threads_size);//預先給線程容器分配空間for (int i = 0; i < m_init_threads_size; ++i){//創建線程并加入線程容器(線程函數為thread_loop)m_threads.push_back(new std::thread(std::bind(&thread_pool::thread_loop, this)));}}//停止線程池void thread_pool::stop(){__SOLA_LOG(debug, "thread_pool::stop() stop.");{std::unique_lock<std::mutex> lock(m_mutex);m_is_started = false;//通知所有線程停止m_cond.notify_all();__SOLA_LOG(debug, "thread_pool::stop() notifyAll().");}for (threads_t::iterator it = m_threads.begin(); it != m_threads.end() ; ++it){(*it)->join(); //等待線程退出delete *it; //釋放線程資源}m_threads.clear();//清空線程容器}//線程池線程函數void thread_pool::thread_loop(){__SOLA_LOG(debug, "thread_pool::threadLoop() tid : " + get_tid() + " start.");//線程池線程循環while(m_is_started){//從任務隊列中取出一個任務task_t task = take();//如果取出的任務不為空,則執行任務(std::function類型可以直接判斷是否為空!)if(task){//執行任務:回調實際的任務函數task();}}__SOLA_LOG(debug, "thread_pool::threadLoop() tid : " + get_tid() + " exit.");}//向線程池添加任務void thread_pool::add_task(const task_t& task){std::unique_lock<std::mutex> lock(m_mutex);/*while(m_tasks.isFull()){//when m_tasks have maxsizecond2.notify_one();}*///向任務隊列中添加任務m_tasks.push_back(task);//通知一個線程m_cond.notify_one();}//從線程池取出一個任務thread_pool::task_t thread_pool::take(){std::unique_lock<std::mutex> lock(m_mutex);//always use a while-loop, due to spurious wakeup//如果任務隊列為空 + 線程池沒有停止,則等待while(m_tasks.empty() && m_is_started){__SOLA_LOG(debug, "thread_pool::take() tid : " + get_tid() + " wait.");m_cond.wait(lock);}__SOLA_LOG(debug, "thread_pool::take() tid : " + get_tid() + " wakeup.");task_t task;tasks_t::size_type size = m_tasks.size();//如果任務隊列不為空 + 線程池沒有停止,則取出一個任務if(!m_tasks.empty() && m_is_started){task = m_tasks.front();m_tasks.pop_front();assert(size - 1 == m_tasks.size());/*if (TaskQueueSize_ > 0){cond2.notify_one();}*/}return task;}
}

3、測試main函數

#include <iostream>
#include <chrono>
#include <condition_variable>
#include "thread_pool.hpp"std::mutex g_mutex;void priorityFunc()
{for (int i = 1; i < 4; ++i){std::this_thread::sleep_for(std::chrono::seconds(1));std::lock_guard<std::mutex> lock(g_mutex);std::cout << "priorityFunc() [" << i << "] at thread [ " << std::this_thread::get_id() << "] output" << std::endl;}}void testFunc()
{// loop to print character after a random period of timefor (int i = 1; i < 4; ++i){std::this_thread::sleep_for(std::chrono::seconds(1));std::lock_guard<std::mutex> lock(g_mutex);std::cout << "testFunc() [" << i << "] at thread [ " << std::this_thread::get_id() << "] output" << std::endl;}}int main()
{sola::active_logger = std::unique_ptr<sola::logger>(new sola::logger(sola::logger::log_level::debug));sola::thread_pool thread_pool;// add tasks to the thread poolfor(int i = 0; i < 2 ; i++)thread_pool.add_task(testFunc);system("pause");return 0;
}

4、執行流程

1、主線程構造函數
thread_pool thread_pool;
{//構造函數中自動啟動線程池start();
}start()
{//創建線程并加入線程容器(線程函數為thread_loop)m_threads.push_back(new std::thread(std::bind(&thread_pool::thread_loop, this)));
}//線程池線程函數
void thread_pool::thread_loop()
{//線程池線程循環while(m_is_started){//從任務隊列中取出一個任務task_t task = take();//如果取出的任務不為空,則執行任務(std::function類型可以直接判斷是否為空!)if(task){//執行任務:回調實際的任務函數task();}}
}//等待:從線程池取出一個任務
thread_pool::task_t thread_pool::take()
{//如果任務隊列為空 + 線程池沒有停止,則等待while(m_tasks.empty() && m_is_started){m_cond.wait(lock);}
}2、主線程中添加任務,到任務隊列
thread_pool.add_task(testFunc);thread_pool::add_task(testFunc)
{//向任務隊列中添加任務m_tasks.push_back(task);//通知一個線程m_cond.notify_one();
}//wait條件滿足:從線程池取出一個任務
thread_pool::task_t thread_pool::take()
{//如果任務隊列為空 + 線程池沒有停止,則等待while(m_tasks.empty() && m_is_started){m_cond.wait(lock);}//如果任務隊列不為空 + 線程池沒有停止,則取出一個任務if(!m_tasks.empty() && m_is_started){task = m_tasks.front();m_tasks.pop_front();}return task;
}//線程池線程函數:執行線程的任務函數
void thread_pool::thread_loop()
{//線程池線程循環while(m_is_started){//從任務隊列中取出一個任務task_t task = take();//如果取出的任務不為空,則執行任務(std::function類型可以直接判斷是否為空!)if(task){//執行任務:回調實際的任務函數task();}}
}//task()回調任務函數
void testFunc()
{
}3、主線程執行完。return 0; // 主線程等待所有工作線程完成(在析構函數中處理)//3.1 調用線程池析構函數
~ThreadPool() 
{
//1.調用stop
stop()
{//1.1通知所有線程停止m_cond.notify_all();=》//1.2使得線程循環退出!while(true){//while循環一直在這等待!(lock滿足條件,向下執行!)//如果任務隊列為空 + 線程池沒有停止,則等待while(m_tasks.empty() && m_is_started){m_cond.wait(lock);}//當線程任務執行完成:stop = true;//線程池停止且任務隊列為空,退出線程循環//如果任務隊列不為空 + 線程池沒有停止,則取出一個任務if(!m_tasks.empty() && m_is_started){task = m_tasks.front();m_tasks.pop_front();}return task;}1.3//等待所有線程完成任務(*it)->join(); //等待線程退出
}4、主線程結束

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

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

相關文章

Java 通過 HttpURLConnection發送 http 請求

問題&#xff1a; 在調試 kill 接口的時候&#xff0c;對方的服務用的是 Django RestFramework 框架提供的接口&#xff0c;用 python 請求時得到的內容如下&#xff1a; ? ~ python3 test.py <Response [200]> "true" // 對應的代碼是 print(response, r…

【PTA數據結構 | C語言版】列出連通集

本專欄持續輸出數據結構題目集&#xff0c;歡迎訂閱。 文章目錄題目代碼題目 給定一個有 n 個頂點和 m 條邊的無向圖&#xff0c;請用深度優先遍歷&#xff08;DFS&#xff09;和廣度優先遍歷&#xff08;BFS&#xff09;分別列出其所有的連通集。假設頂點從 0 到 n?1 編號。…

GoLang教程005:switch分支

3.4 Switch分支 在 GoLand&#xff08;其實是 JetBrains 開發的 Go 編程語言 IDE&#xff09;中&#xff0c;switch 是 Go 語言&#xff08;Golang&#xff09; 的一個重要控制結構&#xff0c;用于替代多個 if-else 語句。 ? 特點說明特性說明自動 breakGo 的 switch 語句默認…

uniapp相關地圖 API調用

目錄 一、 注意事項&#xff1a; manifest.json需增加配置 二、獲取用戶收貨地址 [uni.chooseAddress] 三、獲取當前的地理位置、速度 [uni.getLocation] 四、打開地圖選擇位置、查看位置(導航) [uni.chooseLocation] [uni.openLocation] 五、使用騰訊地圖逆地址解析接口實…

Java學習----NIO模型

在 Java 的 I/O 模型中&#xff0c;NIO&#xff08;Non - Blocking I/O&#xff0c;非阻塞 I/O&#xff09;是對 BIO 的重要改進。它為高并發場景提供了更高效的處理方式&#xff0c;在眾多 Java 應用中發揮著關鍵作用。NIO模型的核心在于非阻塞和多路復用&#xff0c;其采用 “…

MySQL計數函數count原理分析

前言 統計表中數據的條數是非常常用的操作,但是咱們常用的InnoDB存儲引擎計數函數是現時統計的,所以會出現性能的問題,這次我準備分享計數函數count的原理,保證之后遇到計數方面的問題都可以輕易靈活的解決 與MyISAM存儲引擎相比,MyISAM存儲引擎是自己記錄了表中數據的條數,但…

Day07_網絡編程20250721_大項目

基本代碼&#xff1a;搭建服務器客戶端&#xff0c;要求服務器使用 epoll 模型客戶端使用多線程服務器打開數據庫&#xff0c;表單格式如下name text primary key pswd text not null客戶端做一個簡單的界面&#xff1a;1&#xff1a;注冊2&#xff1a;登錄無論注冊還是登錄&am…

20250721

P5357 【模板】AC 自動機 - 洛谷 主要是構建fail樹 /* 我們可以知道的是&#xff0c;當訪問一個點x時&#xff0c;接下來需要跳轉其fail[x]&#xff0c;以此類推&#xff0c;如果在某個fail[x]上出現了一個字符串&#xff0c;那么相應的統計次數應該加1&#xff0c;然后當訪…

【INT四則優先算式】2022-9-22

緣由ccf201903-2二十四點我用暴力破解做的&#xff0c;但是兩個程序一個拿到了滿分&#xff0c;一個拿到了50分&#xff0c;看了很長時間也沒看出問題在哪里&#xff0c;希望有英雄慧眼幫我看一下-編程語言-CSDN問答 void INT四則優先算式() {//緣由https://ask.csdn.net/ques…

本地k8s集群的搭建

windows機器&#xff0c;考慮如果使用云服務器&#xff0c;每年的開銷還是太大&#xff0c;不值得&#xff0c;自己只是做demo&#xff0c;了解各種配置和使用即可&#xff0c;使用VMware的虛擬機來搭建k8s集群 使用docker安裝rancher和k8s yum -y install chronycat > /et…

B樹、B+樹的區別及MySQL為何選擇B+樹

B樹與B樹 B樹和B樹都是自平衡的多路搜索樹&#xff0c;廣泛應用于數據庫和文件系統中&#xff0c;用于高效管理大量數據。它們的設計目標是在磁盤存儲環境下減少I/O操作次數&#xff0c;提高數據訪問效率。下面我將逐步解釋兩者的定義、特性、比較以及應用場景&#xff0c;確保…

Unity之可視化編程VisualScripting快速入門

文章目錄 前言 腳本機和狀態機 腳本圖ScriptGraph 腳本圖 子圖 自定義事件 狀態圖StateGraph 狀態圖 Start狀態 創建新狀態 過渡連接 常用功能 射線檢測 補間動畫 按鈕點擊 前言 可視化腳本使您無需編寫代碼即可為游戲或應用程序創建邏輯。可視化腳本使用基于節點的可視化圖形…

2025三掌柜贈書活動第二十五期 網絡安全應急響應實戰

目錄 前言 網絡安全的重要性 關于《網絡安全應急響應實戰》 編輯推薦 內容簡介 作者簡介 圖書目錄 《網絡安全應急響應實戰》全書速覽 結束語 前言 在當今數字化時代&#xff0c;網絡安全已經成為企業和個人都無法忽視的重要問題。隨著網絡技術的飛速發展&#xff0c;…

車載軟件架構 --- 軟件開發面臨的問題

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 周末洗了一個澡,換了一身衣服,出了門卻不知道去哪兒,不知道去找誰,漫無目的走著,大概這就是成年人最深的孤獨吧! 舊人不知我近況,新人不知我過…

MySQL 8.0 OCP 1Z0-908 題目解析(31)

題目121 Choose two. Examine this command, which executes successfully on InnoDB Cluster: dba.dropMetadataSchema() Which two statements are true? □ A) The mysql_innodb_cluster_metadata schema is dropped from the instance where the connection was establish…

本地生活服務 app 同城信息發布系統搭建

一、邏輯分析用戶需求層面&#xff1a;對于發布者來說&#xff0c;需要一個便捷的界面來輸入同城信息&#xff0c;包括但不限于房屋租售、招聘求職、二手交易、活動推廣等各類信息。發布者要能夠上傳相關圖片、詳細描述信息內容、設置價格&#xff08;如果有需要&#xff09;、…

[Python] -項目實戰4- 利用Python進行Excel批量處理

一、為什么要批量處理Excel文件? 節省時間:人工對數十、數百個 Excel 文件重復操作不現實,Python 批量處理一次搞定。 保證一致性:統一格式、統一操作,避免手動誤差。 易于集成:可嵌入日常自動化流程,支持定時和觸發執行。 二、常用庫及選型建議 庫 作用 優勢 局限 p…

社區搜索離線回溯系統設計:架構、挑戰與性能優化|得物技術

一、項目背景在社區場景中&#xff0c;我們積累了豐富的用戶互動數據。這些歷史互動信息對CTR/CVR預估建模具有重要參考價值&#xff0c;用戶的每次互動都反映了其特定維度的偏好特征。當前&#xff0c;已在多個業務實踐中驗證&#xff0c;基于用戶歷史互動特征進行未來行為預測…

WPF——自定義ListBox

在閱讀本文前&#xff0c;最好先看看WPF——自定義RadioButton 背景 WPF中實現單選功能通常有兩種方案&#xff1a; - RadioButton組&#xff1a;傳統方案&#xff0c;但代碼冗余 - ListBox定制&#xff1a;通過樣式改造&#xff0c;兼顧數據綁定和UI靈活性 需求 一組選項中…

rancher上使用rke在華為云多網卡的服務器上安裝k8s集群問題處理了

報錯:問題&#xff1a;[[network] Host [192.168.0.213] is not able to connect to the following ports: [192.168.0.213:2379]. Please check network policies and firewall rules]問題&#xff1a; roothwy-isms-210-66:~# gotelnet 172.17.210.66 2379 map[2379:failed] …