設計模式(C++)詳解—單例模式(2)

<摘要>
單例模式是創建型設計模式中最簡單但應用最廣泛的模式之一,它確保一個類只有一個實例并提供全局訪問點。本文從歷史背景和核心概念出發,系統闡述了單例模式的產生緣由和演進脈絡,深入剖析了其在資源管理、狀態一致性和訪問控制方面的設計意圖。通過餓漢式、懶漢式、Meyer’s Singleton和線程安全雙檢鎖等四種經典實現方式的對比分析,結合日志系統、配置管理、數據庫連接池等實際應用場景,全面展示了單例模式的具體實現技術和適用場景。文章特別針對多線程環境下的線程安全問題進行了深度解析,使用時序圖和狀態圖直觀演示了競態條件的產生與解決方案,最后探討了單例模式的測試方法和替代方案,為開發者提供了全面而實用的指導。


<解析>

1. 背景與核心概念

1.1 產生背景與發展脈絡

單例模式(Singleton Pattern)的誕生源于軟件開發中對特定類型對象管理的實際需求。在早期的軟件開發實踐中,開發者逐漸意識到某些類的實例應該在整個應用程序生命周期中只存在一個,這種需求催生了單例模式的形成。

歷史演進階段

  1. 初期探索階段(1980年代前):在面向對象編程范式普及之前,開發者通常使用全局變量來實現類似單例的功能。這種方式雖然簡單,但帶來了命名沖突、初始化順序不確定和訪問控制缺失等問題。

  2. 模式化階段(1980-1990年代):隨著"Gang of Four"(Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides)在1994年出版的《設計模式:可復用面向對象軟件的基礎》一書中正式提出單例模式,它被系統性地歸納為23種經典設計模式之一,屬于創建型模式類別。

  3. 語言特性融合階段(2000年代至今):隨著編程語言的發展,單例模式的實現方式不斷演進。C++11標準引入的線程局部存儲(thread_local)、原子操作(atomic)和靜態變量初始化線程安全等特性,為單例模式提供了更優雅的實現方案。

產生的根本原因

  • 資源共享需求:如數據庫連接池、線程池等需要集中管理的資源
  • 狀態一致性要求:如配置管理、計數器等需要全局一致狀態的對象
  • 性能優化考慮:避免頻繁創建銷毀重量級對象帶來的開銷
  • 訪問控制需要:集中管控對特定資源的訪問,如日志系統

1.2 核心概念與關鍵術語

單例模式(Singleton Pattern):確保一個類只有一個實例,并提供一個全局訪問點來獲取該實例的設計模式。

關鍵特性

  • 唯一實例性(Instance Uniqueness):保證類只有一個實例存在
  • 全局可訪問性(Global Accessibility):提供統一的訪問入口
  • 延遲初始化(Lazy Initialization):多數實現支持在第一次使用時創建實例
  • 線程安全性(Thread Safety):在多線程環境下保證正確性

基本結構組件

class Singleton {
private:static Singleton* instance;    // 靜態私有成員,保存唯一實例Singleton();                   // 私有構造函數,防止外部實例化Singleton(const Singleton&) = delete;            // 刪除拷貝構造函數Singleton& operator=(const Singleton&) = delete; // 刪除賦值運算符public:static Singleton* getInstance(); // 靜態公共方法,提供全局訪問點// 其他成員函數...
};

UML表示

Singleton
-static Singleton* instance
-Singleton()
+static getInstance()
+someOperation() : void

圖1.1:單例模式基本UML類圖

2. 設計意圖與考量

2.1 核心設計目標

單例模式的設計旨在解決以下核心問題:

2.1.1 controlled Instance Creation(受控實例創建)
通過將構造函數設為私有,單例模式徹底消除了客戶端隨意創建類實例的可能性。這種強制性的創建控制確保了實例數量的嚴格管理,從語言機制層面而非僅僅約定層面保證了單一實例的約束。

2.1.2 Global Access Point(全局訪問點)
提供靜態方法getInstance()作為獲取單例實例的統一入口,解決了全局變量方式的散亂訪問問題。這種方法:

  • 明確了職責:清晰標識這是獲取實例的正確方式
  • 封裝了復雜性:隱藏了實例創建和管理的細節
  • 提供了靈活性:允許在不改變客戶端代碼的情況下修改實例化策略

2…3 Resource Coordination(資源協調)
對于需要協調共享資源的場景,單例模式提供了自然的設計方案:

  • 避免資源沖突:如多個日志寫入器同時寫文件可能導致的內容交錯
  • 減少資源浪費:如數據庫連接的重用而非重復創建
  • 統一管理策略:如緩存的一致性管理和過期策略

2.2 設計考量因素

2.2.1 線程安全性考量
在多線程環境下,單例模式的實現必須考慮競爭條件(Race Condition)問題:

Thread1Thread2Singleton競態條件發生場景getInstance()檢查instance==nullptrgetInstance()檢查instance==nullptrnew Singleton()創建實例new Singleton()創建另一個實例兩個線程得到不同實例,違反單例原則Thread1Thread2Singleton

圖2.1:多線程環境下的競態條件時序圖

解決方案包括:

  • 餓漢式初始化:在程序啟動時即創建實例,避免運行時競爭
  • 互斥鎖保護:在懶漢式初始化時使用鎖機制
  • 雙檢鎖模式:減少鎖的使用頻率,提高性能
  • 局部靜態變量:利用C++11的靜態變量線程安全特性

2.2.2 初始化時機權衡

初始化方式優點缺點適用場景
餓漢式實現簡單,線程安全可能提前占用資源,啟動慢實例小且必定使用
懶漢式資源按需分配,啟動快實現復雜,需要線程安全措施實例大或不一定會使用

2.2.3 繼承與擴展性
單例類的繼承會帶來設計上的挑戰:

  • 構造函數隱私性:派生類需要訪問基類構造函數
  • 實例唯一性:每個派生類是否都應該是單例?
  • 模板方法應用:通過模板元編程實現可復用的單例基類

2.2.4 測試困難性
單例模式對單元測試不友好,主要原因:

  • 全局狀態共享:測試用例之間可能相互影響
  • 難以模擬:無法輕松替換為模擬對象進行測試
  • 重置困難:需要額外機制在測試間重置單例狀態

2.2.5 生命周期管理
單例實例的生命周期管理需要考慮:

  • 創建時機:何時以及如何創建實例
  • 銷毀時機:是否需要顯式銷毀,如何保證安全銷毀
  • 依賴關系:單例之間的依賴關系及初始化順序

3. 實例與應用場景

3.1 日志系統(Logger)

應用場景
在大多數應用程序中,日志系統需要滿足以下要求:

  • 全局唯一:多個模塊共享同一個日志實例
  • 線程安全:多線程環境下能安全寫入日志
  • 集中配置:統一設置日志級別、輸出目標等

實現方案1:Meyer’s Singleton(C++11及以上)

class Logger {
public:static Logger& getInstance() {static Logger instance;  // C++11保證靜態局部變量初始化線程安全return instance;}void log(const std::string& message, LogLevel level = LogLevel::INFO) {std::lock_guard<std::mutex> lock(logMutex);// 實際日志記錄邏輯std::cout << "[" << getLevelString(level) << "] " << message << std::endl;}void setLogLevel(LogLevel level) { /* 實現 */ }// 刪除拷貝構造函數和賦值運算符Logger(const Logger&) = delete;Logger& operator=(const Logger&) = delete;private:std::mutex logMutex;LogLevel currentLevel;Logger() : currentLevel(LogLevel::INFO) {// 初始化邏輯}~Logger() {// 清理邏輯,如關閉文件等}std::string getLevelString(LogLevel level) { /* 實現 */ }
};// 使用示例
Logger::getInstance().log("Application started");
Logger::getInstance().setLogLevel(LogLevel::DEBUG);

實現方案2:帶雙檢鎖的懶漢式

class Logger {
public:static Logger* getInstance() {Logger* tmp = instance.load(std::memory_order_acquire);if (tmp == nullptr) {std::lock_guard<std::mutex> lock(mutex);tmp = instance.load(std::memory_order_relaxed);if (tmp == nullptr) {tmp = new Logger();instance.store(tmp, std::memory_order_release);}}return tmp;}// 其他成員函數同上...private:static std::atomic<Logger*> instance;static std::mutex mutex;Logger() { /* 初始化 */ }~Logger() { /* 清理 */ }
};// 靜態成員初始化
std::atomic<Logger*> Logger::instance{nullptr};
std::mutex Logger::mutex;

3.2 配置管理器(Configuration Manager)

應用場景
應用程序通常需要讀取和管理配置文件,配置管理器應該:

  • 全局唯一:確保所有模塊使用相同的配置
  • 懶加載:只在第一次使用時加載配置
  • 線程安全:支持多線程并發讀取配置

實現方案:帶異常處理的單例

class ConfigManager {
public:static ConfigManager& getInstance() {try {static ConfigManager instance;return instance;} catch (const std::exception& e) {// 處理初始化異常std::cerr << "ConfigManager initialization failed: " << e.what() << std::endl;throw;}}void loadConfig(const std::string& filename) {std::lock_guard<std::mutex> lock(configMutex);// 解析配置文件// 可能拋出異常,如文件不存在或格式錯誤}std::string getValue(const std::string& key, const std::string& defaultValue = "") const {std::lock_guard<std::mutex> lock(configMutex);auto it = configMap.find(key);return it != configMap.end() ? it->second : defaultValue;}void setValue(const std::string& key, const std::string& value) {std::lock_guard<std::mutex> lock(configMutex);configMap[key] = value;}private:std::mutex configMutex;std::unordered_map<std::string, std::string> configMap;ConfigManager() {// 嘗試加載默認配置try {loadConfig("default.conf");} catch (...) {// 使用內置默認值setDefaultValues();}}void setDefaultValues() {configMap["server.host"] = "localhost";configMap["server.port"] = "8080";// 更多默認值...}// 禁止拷貝和賦值ConfigManager(const ConfigManager&) = delete;ConfigManager& operator=(const ConfigManager&) = delete;
};// 使用示例
std::string host = ConfigManager::getInstance().getValue("server.host");
int port = std::stoi(ConfigManager::getInstance().getValue("server.port"));

3.3 數據庫連接池(Database Connection Pool)

應用場景
數據庫連接是昂貴的資源,連接池需要:

  • 限制連接數量:防止過多連接耗盡數據庫資源
  • 重用連接:避免頻繁創建和關閉連接
  • 全局管理:所有數據庫操作共享同一個連接池

實現方案:帶連接管理的單例

class ConnectionPool {
public:static ConnectionPool& getInstance() {static ConnectionPool instance;return instance;}std::shared_ptr<DatabaseConnection> getConnection() {std::unique_lock<std::mutex> lock(poolMutex);// 等待可用連接connectionAvailable.wait(lock, [this]() {return !availableConnections.empty() || currentSize < maxSize;});if (!availableConnections.empty()) {auto conn = availableConnections.front();availableConnections.pop();return std::shared_ptr<DatabaseConnection>(conn, [this](DatabaseConnection* conn) {releaseConnection(conn);});}if (currentSize < maxSize) {auto conn = createConnection();currentSize++;return std::shared_ptr<DatabaseConnection>(conn, [this](DatabaseConnection* conn) {releaseConnection(conn);});}throw std::runtime_error("Unable to get database connection");}void configure(size_t maxConnections, const std::string& connectionString) {std::lock_guard<std::mutex> lock(poolMutex);this->maxSize = maxConnections;this->connectionString = connectionString;// 可選的預創建連接precreateConnections();}private:std::mutex poolMutex;std::condition_variable connectionAvailable;std::queue<DatabaseConnection*> availableConnections;size_t currentSize = 0;size_t maxSize = 10;std::string connectionString;ConnectionPool() = default;~ConnectionPool() {cleanup();}DatabaseConnection* createConnection() {// 實際創建數據庫連接的邏輯return new DatabaseConnection(connectionString);}void releaseConnection(DatabaseConnection* conn) {std::lock_guard<std::mutex> lock(poolMutex);if (conn->isValid()) {availableConnections.push(conn);connectionAvailable.notify_one();} else {delete conn;currentSize--;connectionAvailable.notify_one();}}void precreateConnections() {for (size_t i = 0; i < std::min(size_t(3), maxSize); ++i) {availableConnections.push(createConnection());currentSize++;}}void cleanup() {while (!availableConnections.empty()) {delete availableConnections.front();availableConnections.pop();}}// 禁止拷貝和賦值ConnectionPool(const ConnectionPool&) = delete;ConnectionPool& operator=(const ConnectionPool&) = delete;
};// 使用示例
auto& pool = ConnectionPool::getInstance();
pool.configure(20, "host=localhost;dbname=test;user=root");
auto connection = pool.getConnection();
// 使用connection進行數據庫操作...

3.4 狀態管理器(State Manager)

應用場景
在游戲或復雜應用中,需要管理全局狀態:

  • 全局可訪問:各個子系統需要訪問和修改狀態
  • 線程安全:多線程環境下的狀態更新
  • 狀態持久化:支持狀態的保存和恢復

實現方案:觀察者模式結合的單例

class GameStateManager {
public:static GameStateManager& getInstance() {static GameStateManager instance;return instance;}// 狀態獲取和設置int getScore() const {std::shared_lock<std::shared_mutex> lock(stateMutex);return currentState.score;}void setScore(int score) {{std::unique_lock<std::shared_mutex> lock(stateMutex);currentState.score = score;}notifyObservers(StateEvent::SCORE_CHANGED);}// 觀察者模式支持void addObserver(StateObserver* observer) {std::lock_guard<std::mutex> lock(observerMutex);observers.push_back(observer);}void removeObserver(StateObserver* observer) {std::lock_guard<std::mutex> lock(observerMutex);observers.erase(std::remove(observers.begin(), observers.end(), observer),observers.end());}// 狀態持久化bool saveState(const std::string& filename) const {std::shared_lock<std::shared_mutex> lock(stateMutex);// 序列化狀態到文件return true;}bool loadState(const std::string& filename) {GameState newState;// 從文件加載狀態{std::unique_lock<std::shared_mutex> lock(stateMutex);currentState = newState;}notifyObservers(StateEvent::STATE_LOADED);return true;}private:mutable std::shared_mutex stateMutex;std::mutex observerMutex;struct GameState {int score = 0;int level = 1;std::string playerName;// 更多狀態字段...} currentState;std::vector<StateObserver*> observers;GameStateManager() = default;~GameStateManager() = default;void notifyObservers(StateEvent event) {std::vector<StateObserver*> observersCopy;{std::lock_guard<std::mutex> lock(observerMutex);observersCopy = observers;}for (auto observer : observersCopy) {observer->onStateChanged(event);}}// 禁止拷貝和賦值GameStateManager(const GameStateManager&) = delete;GameStateManager& operator=(const GameStateManager&) = delete;
};// 使用示例
GameStateManager::getInstance().setScore(1000);
int currentScore = GameStateManager::getInstance().getScore();

4. 交互性內容解析

4.1 多線程環境下的交互分析

單例模式在多線程環境下的行為復雜性主要體現在實例化過程中。以下通過時序圖詳細分析不同實現方式的線程交互:

4.1.1 不安全懶漢式的競態條件

ThreadAThreadBSingletonClass不安全懶漢式初始化getInstance()檢查instance==nullptr確認instance為nullptrgetInstance()檢查instance==nullptr確認instance為nullptr(此時還未創建)new Singleton()創建實例賦值給instancenew Singleton()創建另一個實例賦值給instance(覆蓋前一個)產生兩個實例,內存泄漏ThreadAThreadBSingletonClass

4.1.2 雙檢鎖模式的正確交互

ThreadAThreadBMutexSingletonClassgetInstance()檢查instance==nullptr確認instance為nullptrlock()獲取互斥鎖getInstance()檢查instance==nullptr等待(因為instance仍為nullptr)再次檢查instance==nullptr(雙檢)new Singleton()創建實例原子操作賦值給instanceunlock()釋放互斥鎖lock()獲取互斥鎖再次檢查instance≠nullptr(雙檢)unlock()釋放互斥鎖返回已創建的實例ThreadAThreadBMutexSingletonClass

4.2 單例與依賴組件的交互

在實際應用中,單例對象往往需要與其他系統組件進行交互。以下以日志單例為例展示其與文件系統、網絡服務的交互:

ClientLoggerSingletonFileSystemNetworkServicelog("Error occurred", ERROR)獲取當前時間戳格式化日志消息write(log_file, message)寫入成功sendLog(message)發送確認alt[網絡日志啟用]日志記錄完成ClientLoggerSingletonFileSystemNetworkService

5. 高級主題與最佳實踐

5.1 單例模式的變體

5.1.1 多例模式(Multiton)
擴展單例概念,允許有限數量的實例,通常按鍵區分:

template<typename Key, typename Value>
class Multiton {
public:static Value& getInstance(const Key& key) {std::lock_guard<std::mutex> lock(mutex);auto it = instances.find(key);if (it == instances.end()) {it = instances.emplace(key, std::make_unique<Value>()).first;}return *it->second;}// 禁止外部構造和拷貝Multiton() = delete;Multiton(const Multiton&) = delete;Multiton& operator=(const Multiton&) = delete;private:static std::mutex mutex;static std::map<Key, std::unique_ptr<Value>> instances;
};// 使用示例
auto& config1 = Multiton<std::string, ConfigManager>::getInstance("database");
auto& config2 = Multiton<std::string, ConfigManager>::getInstance("application");

5.1.2 線程局部單例(Thread-Local Singleton)
每個線程擁有自己的單例實例:

class ThreadLocalLogger {
public:static ThreadLocalLogger& getInstance() {thread_local ThreadLocalLogger instance;return instance;}void log(const std::string& message) {// 線程安全的日志記錄,無需加鎖logs.push_back(message);}std::vector<std::string> getLogs() const {return logs;}private:std::vector<std::string> logs;ThreadLocalLogger() = default;~ThreadLocalLogger() = default;// 禁止拷貝和賦值ThreadLocalLogger(const ThreadLocalLogger&) = delete;ThreadLocalLogger& operator=(const ThreadLocalLogger&) = delete;
};

5.2 單例模式的測試策略

由于單例的全局狀態特性,對其進行單元測試需要特殊策略:

5.2.1 測試夾具設計

class ConfigManagerTest : public ::testing::Test {
protected:void SetUp() override {// 保存原始實例(如果支持重置)originalInstance = &ConfigManager::getInstance();// 使用測試配置ConfigManager::getInstance().loadConfig("test_config.conf");}void TearDown() override {// 重置單例狀態ConfigManager::getInstance().resetToDefaults();}ConfigManager* originalInstance;
};TEST_F(ConfigManagerTest, LoadsConfigurationCorrectly) {auto& config = ConfigManager::getInstance();EXPECT_EQ(config.getValue("test.setting"), "expected_value");
}

5.2.2 可測試單例設計
通過引入依賴注入和接口抽象增強可測試性:

class IConfigManager {
public:virtual ~IConfigManager() = default;virtual std::string getValue(const std::string& key) const = 0;virtual void setValue(const std::string& key, const std::string& value) = 0;
};class ConfigManager : public IConfigManager {
public:static IConfigManager& getInstance() {static ConfigManager instance;return instance;}// 實現接口方法...// 測試支持方法static void setTestInstance(IConfigManager* testInstance) {testInstanceOverride = testInstance;}static void resetInstance() {testInstanceOverride = nullptr;}// 通過此方法訪問實例,允許測試替換static IConfigManager& getInstanceInternal() {if (testInstanceOverride != nullptr) {return *testInstanceOverride;}return getInstance();}private:static IConfigManager* testInstanceOverride;ConfigManager() = default;// 其他實現...
};// 在測試中
class MockConfigManager : public IConfigManager {
public:MOCK_METHOD(std::string, getValue, (const std::string&), (const override));MOCK_METHOD(void, setValue, (const std::string&, const std::string&), (override));
};TEST(ConfigDependentTest, UsesConfigManager) {MockConfigManager mockConfig;EXPECT_CALL(mockConfig, getValue("test.key")).WillOnce(Return("mock_value"));ConfigManager::setTestInstance(&mockConfig);// 測試使用ConfigManager::getInstanceInternal()的代碼// ...ConfigManager::resetInstance();
}

5.3 單例模式的替代方案

雖然單例模式有用,但并非所有全局訪問需求都適合使用單例。考慮以下替代方案:

5.3.1 依賴注入(Dependency Injection)
通過構造函數或方法參數顯式傳遞依賴:

class Application {
public:// 通過構造函數注入依賴explicit Application(ILogger& logger, IConfigManager& config): logger(logger), config(config) {}void run() {logger.log("Application started");std::string setting = config.getValue("some_setting");// ...}private:ILogger& logger;IConfigManager& config;
};// 在組合根中組裝對象
int main() {auto& logger = Logger::getInstance();auto& config = ConfigManager::getInstance();Application app(logger, config);app.run();
}

5.3.2 單例服務定位器(Service Locator)
提供全局訪問點,但允許替換實現:

class ServiceLocator {
public:static ILogger& getLogger() {ILogger* service = loggerService.load();if (service == nullptr) {// 返回默認實現或拋出異常return defaultLogger;}return *service;}static void registerLogger(ILogger* service) {loggerService.store(service);}static void deregisterLogger() {loggerService.store(nullptr);}private:static std::atomic<ILogger*> loggerService;static DefaultLogger defaultLogger;
};// 使用示例
ServiceLocator::getLogger().log("Message");// 在測試中
TEST(SomeTest, TestWithMockLogger) {MockLogger mockLogger;ServiceLocator::registerLogger(&mockLogger);// 執行測試...ServiceLocator::deregisterLogger();
}

6. 總結與建議

6.1 單例模式適用場景

在以下情況下考慮使用單例模式:

  1. 確需全局唯一實例的場景
  2. 需要嚴格控制實例數量的資源管理
  3. 需要集中管理全局狀態或配置
  4. 頻繁訪問的重量級對象需要重用

6.2 單例模式實現選擇建議

場景推薦實現理由
C++11及以上環境Meyer’s Singleton簡單、安全、自動銷毀
需要控制初始化時機雙檢鎖模式精確控制初始化時機
性能敏感場景餓漢式無運行時開銷,但可能浪費資源
需要參數化初始化帶init方法的單例支持初始化參數傳遞

6.3 注意事項與陷阱

  1. 隱藏的依賴:單例模式會創建隱藏的全局依賴,降低代碼可測試性和模塊化
  2. 生命周期管理:注意單例的銷毀順序,特別是在靜態銷毀期訪問單例
  3. 過度使用:避免將單例作為全局變量的替代品,導致設計僵化
  4. 線程安全:確保實現正確的線程同步,避免競態條件

6.4 現代C++中的改進

C++11及以上版本提供了更好的單例實現工具:

  • thread_local:實現線程局部單例
  • std::call_once:保證一次性初始化
  • 原子操作:實現無鎖或低鎖同步
  • 靜態局部變量:線程安全的延遲初始化

單例模式是強大的工具,但需要謹慎使用。正確應用時,它可以提供優雅的解決方案;濫用時,它會導致代碼難以維護和測試。始終考慮是否真的需要單例,或者是否有更好的替代設計方案。

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

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

相關文章

kafka如何保證消息的順序性

kafka如何保證消息的順序性 Kafka只能在分區&#xff08;Partition&#xff09;級別保證消息的順序性&#xff0c;而不能在主題&#xff08;Topic&#xff09;級別保證全局順序。 核心原理&#xff1a;分區和偏移量分區&#xff08;Partition&#xff09;是順序性的基礎&#x…

傳輸層:UDP/TCP協議

網絡協議圖 一.UDP 特點: 無連接&#xff0c;不可靠&#xff0c;面向數據報&#xff0c;全雙工&#xff08;前面網絡編程中介紹過&#xff09; 格式: 服務器的端口號一般都是程序員指定的(這樣你才能訪問到),客戶端的端口號是系統自動分配的(如果提前指定好, 可能會與其他程…

A/B測試全解析:原理、流程與實戰案例

A/B測試&#xff08;AB Testing&#xff09;原理與實踐全解析 在數據驅動的時代&#xff0c;A/B測試幾乎是每一個互聯網公司都會使用的實驗方法。無論是電商平臺優化轉化率&#xff0c;還是內容平臺提升點擊率&#xff0c;抑或是游戲公司提升留存&#xff0c;A/B測試都是最常見…

循環神經網絡(三):小練習

RNN小練習 要求&#xff1a; 假設有 4 個字 吃 了 沒 &#xff1f;&#xff0c;請使用 torch.nn.RNN 完成以下任務 將每個進行 one-hot 編碼請使用 吃 了 沒 作為輸入序列&#xff0c;了 沒 &#xff1f; 作為輸出序列RNN 的 hidden_size 64請將 RNN 的輸出使用全連接轉換成 4…

ESPIDF官方文檔,啟用dhcp會禁用對應的STA或AP的靜態IP,我測試STA確實是,但是AP不是,為什么

1. STA 模式下的 DHCP&#xff08;客戶端角色&#xff09;ESP32 當 Station&#xff08;STA&#xff09; 時&#xff0c;它的行為就跟你的手機/筆記本連 Wi-Fi 一樣&#xff1a;DHCP 客戶端 → 去路由器&#xff08;DHCP 服務器&#xff09;要一個 IP。特點啟用 DHCP&#xff0…

cocos2d. 3.17.2 c++如何實現下載斷點續傳zip壓縮包帶進度條

新建類CurlDown #include “curl/curl.h” #include using namespace std; USING_NS_CC; /** 資源下載curl */ class CurlDown { public: CurlDown(); ~CurlDown(); void StartDownResZip(string downLoadUrl, int64_t totalSize); //下載控制 void downloadControler(); //下…

MySQL 整型數據類型:選對數字類型,讓存儲效率翻倍

MySQL 整型數據類型&#xff1a;選對數字類型&#xff0c;讓存儲效率翻倍 在 MySQL 中&#xff0c;整型&#xff08;整數類型&#xff09;是最常用的數據類型之一&#xff0c;從用戶 ID 到商品數量&#xff0c;幾乎所有涉及數字的場景都離不開它。但你知道嗎&#xff1f;選對整…

公司電腦監控軟件有哪些?公司電腦監控軟件應該怎么選擇

大家好呀&#xff0c;電競直播運營團隊常常面臨 “直播腳本被抄襲、用戶付費數據篡改、主播話術外泄” 的問題&#xff01;尤其是獨家直播流程腳本、用戶充值記錄、主播互動話術庫、賽事解說手稿&#xff0c;一旦泄露可能導致競品跟風、用戶信任下降、直播競爭力減弱&#xff5…

ARM裸機開發:鏈接腳本、進階Makefile(bsp)、編譯過程、beep實驗

一、鏈接腳本的作用&#xff1f;各個段存放什么數據類型&#xff08;一&#xff09;鏈接腳本內容SECTIONS {. 0x87800000;.text : {obj/start.o*(.text)}.rodata ALIGN(4) : {*(.rodata*)}.data ALIGN(4) : {*(.data)}__bss_start .;.bss ALIGN(4) : {*(.bss) *(COMMON)}__bs…

Linux驅動開發(1)概念、環境與代碼框架

一、驅動概念驅動與底層硬件直接打交道&#xff0c;充當了硬件與應用軟件中間的橋梁。1、具體任務&#xff08;1&#xff09;讀寫設備寄存器&#xff08;實現控制的方式&#xff09;&#xff08;2&#xff09;完成設備的輪詢、中斷處理、DMA通信&#xff08;CPU與外設通信的方式…

計算機視覺(十):ROI

什么是感興趣區域&#xff08;ROI&#xff09;&#xff1f; 在計算機視覺中&#xff0c;**感興趣區域&#xff08;ROI&#xff09;**指的是圖像中包含我們想要分析、處理或識別的目標或特征的特定子集。就像我們在閱讀一本書時會聚焦于某個重要的段落&#xff0c;計算機視覺系統…

Jenkins 構建 Node 項目報錯解析與解決——pnpm lockfile 問題實戰

在使用 Jenkins 自動化構建 Node.js 項目時&#xff0c;經常會遇到類似報錯&#xff1a; ERR_PNPM_OUTDATED_LOCKFILE? Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with package.json Error: Cannot find module node_module…

Kafka在多環境中安全管理敏感

1. 配置提供者是什么&#xff1f; 配置提供者&#xff08;ConfigProvider&#xff09;是一類按需“拉取配置”的組件&#xff1a;應用讀取配置時&#xff0c;按約定的占位符語法去外部來源&#xff08;目錄、環境變量、單一 properties 文件、你自定義的來源……&#xff09;取…

編程工具的演進邏輯:從Python IDLE到Arduino IDE的深度剖析

引言:工具進化的本質 在編程學習與開發的道路上,我們總會與各種各樣的工具相遇。一個有趣的現象是,無論是初學者的第一款工具Python IDLE,還是硬件愛好者常用的Thonny和Arduino IDE,它們都自稱“集成開發環境”(IDE)。這背后隱藏著怎樣的邏輯? 本文將帶你深入分析這三…

p10k configure執行報錯: ~/powerlevel10k/config/p10k-lean.zsh is not readable

[ERROR] p10k configure: ~/powerlevel10k/config/p10k-lean.zsh is not readable 背景 我移動了Powerlevel10k文件夾的位置&#xff0c;導致p10k configure命令找不到powerlevel10k文件夾的位置。 原來Powerlevel10k的位置&#xff1a;~/powerlevel10k 移動后Powerlevel10k的位…

Java 學習筆記(進階篇3)

1. 美化界面關鍵邏輯 1&#xff1a;// 相對路徑&#xff1a;直接從項目的 src 目錄開始寫&#xff0c;不包含 D:\ 和個人名字 ImageIcon bg new ImageIcon("src/image/background.png"); JLabel background new JLabel(bg);這兩行代碼是 Swing 中加載并顯示圖片的經…

BFD 概述

BFD簡介1.BFD:Bidirectional Forwarding Detection,雙向轉發檢查概述&#xff1a;毫秒級鏈路故障檢查&#xff0c;通常結合三層協議&#xff08;如靜態路由、vrrp、 ospf、 BGP等&#xff09;實現鏈路故障快速切換。作用&#xff1a;① 檢測二層非直連故障② 加快三層協議收斂底…

【嵌入式DIY實例-ESP32篇】-Flappy Bird游戲

Flappy Bird游戲 文章目錄 Flappy Bird游戲 1、游戲介紹 2、硬件準備與接線 3、代碼實現 《Flappy Bird》游戲以其引人入勝的玩法和簡約的設計風靡全球。本文將探討如何使用 OLED SSD1306 顯示屏和 ESP32 微控制器重現這款經典游戲。這個 DIY 項目不僅充滿樂趣,也是學習編程和…

[數據結構——lesson2.順序表]

目錄 學習目標 引言 1.什么是線性表&#xff1f; 2.什么是順序表&#xff1f; 2.1概念及結構 2.2 接口實現 2.2.1順序表的功能 1.順序表的初始化 2.打印數據 3.尾插數據 (1)檢查空間 (2)插入數據 4.尾刪數據 5.頭插數據 6.頭刪數據 7.數據查找 8.指定位置數據…

ChatGPT大模型訓練指南:如何借助動態代理IP提高訓練效率

隨著人工智能技術的飛速發展&#xff0c;ChatGPT等大型語言模型&#xff08;LLM&#xff09;已成為科技界和產業界關注的焦點。模型的訓練過程耗時、耗資源且對網絡環境要求極高。尤其是在需要模擬真實用戶行為、進行大規模數據爬取或分布式訓練的場景下&#xff0c;單一IP地址…