最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Qt優(yōu)雅實(shí)現(xiàn)線程安全單例模式(模板化和自動(dòng)清理)

 更新時(shí)間:2025年11月21日 09:24:03   作者:喵個(gè)咪  
在 Qt 開(kāi)發(fā)中,單例模式是高頻使用的設(shè)計(jì)模式,本文將基于模板封裝 + 管理器的設(shè)計(jì)思路,實(shí)現(xiàn)一套可直接復(fù)用的單例框架,并詳細(xì)講解其設(shè)計(jì)原理與最佳實(shí)踐,有需要的可以了解下

在 Qt 開(kāi)發(fā)中,單例模式是高頻使用的設(shè)計(jì)模式,用于全局共享一個(gè)實(shí)例(如配置管理、網(wǎng)絡(luò)服務(wù)、日志系統(tǒng)等)。一個(gè)健壯的 Qt 單例需要滿足 線程安全、自動(dòng)清理、通用性強(qiáng)、支持任意構(gòu)造參數(shù) 等核心需求。本文將基于模板封裝 + 管理器的設(shè)計(jì)思路,實(shí)現(xiàn)一套可直接復(fù)用的單例框架,并詳細(xì)講解其設(shè)計(jì)原理與最佳實(shí)踐。

一、單例模式的核心訴求

在 Qt 環(huán)境中,單例的設(shè)計(jì)需要解決以下關(guān)鍵問(wèn)題:

  • 線程安全: 多線程并發(fā)調(diào)用時(shí)避免創(chuàng)建多個(gè)實(shí)例;
  • 自動(dòng)清理: 程序退出時(shí)自動(dòng)釋放資源,避免內(nèi)存泄漏(尤其配合 Qt 的 QCoreApplication::aboutToQuit 機(jī)制);
  • 通用性: 支持任意類(lèi)作為單例,無(wú)需重復(fù)編寫(xiě)單例邏輯;
  • 靈活構(gòu)造: 支持帶參數(shù)的構(gòu)造函數(shù),且不丟失參數(shù)語(yǔ)義;
  • 安全校驗(yàn): 避免未初始化就調(diào)用實(shí)例的錯(cuò)誤;
  • 可手動(dòng)控制: 支持主動(dòng)初始化 / 銷(xiāo)毀單例。

本文實(shí)現(xiàn)的單例框架完全滿足以上需求,且兼容 Qt 控制臺(tái)程序、桌面程序等所有場(chǎng)景。

二、實(shí)現(xiàn)架構(gòu)設(shè)計(jì)

整體架構(gòu)分為兩層:

  • Singleton 模板類(lèi): 負(fù)責(zé)單例的實(shí)例創(chuàng)建、線程安全保護(hù)、初始化 / 銷(xiāo)毀邏輯,通過(guò)模板實(shí)現(xiàn)通用性;
  • SingletonManager 單例管理器: 負(fù)責(zé)注冊(cè)所有單例的清理回調(diào),程序退出時(shí)統(tǒng)一執(zhí)行銷(xiāo)毀,避免內(nèi)存泄漏。

核心設(shè)計(jì)思路:

  • 模板化封裝單例邏輯,避免重復(fù)編碼;
  • QMutex 保證多線程下的實(shí)例創(chuàng)建 / 訪問(wèn)安全;
  • 完美轉(zhuǎn)發(fā)(std::forward)支持任意構(gòu)造參數(shù);
  • 清理回調(diào)注冊(cè)到管理器,利用 Qt 的 aboutToQuit 統(tǒng)一觸發(fā);
  • 斷言(Q_ASSERT)+ 宏定義簡(jiǎn)化使用,同時(shí)提供安全校驗(yàn)。

三、完整代碼詳解

3.1 單例管理器:SingletonManager.h

管理器的核心作用是「集中管理所有單例的清理邏輯」,避免每個(gè)單例單獨(dú)處理銷(xiāo)毀,確保退出時(shí)資源釋放的一致性。

#ifndef SINGLETONMANAGER_H
#define SINGLETONMANAGER_H

#include <QMutex>
#include <functional>
#include <map>
#include <QObject> // 確保 Q_DISABLE_COPY_MOVE 可用

// 單例管理器:統(tǒng)一注冊(cè)/注銷(xiāo)/執(zhí)行單例清理回調(diào)
class SingletonManager {
public:
    // 管理器自身是單例(懶加載,線程安全)
    static SingletonManager &instance() {
        static SingletonManager inst; // C++11 后靜態(tài)局部變量初始化線程安全
        return inst;
    }

    /**
     * @brief 注冊(cè)單例清理回調(diào)
     * @param fn 清理函數(shù)(通常是刪除單例實(shí)例的 lambda)
     * @return 注冊(cè) ID(用于后續(xù)注銷(xiāo)),ID > 0
     */
    int registerCleanup(std::function<void()> fn) {
        QMutexLocker locker(&m_mutex); // 加鎖保證線程安全
        int id = m_nextId++;
        m_funcs.emplace(id, std::move(fn)); // 轉(zhuǎn)移函數(shù)所有權(quán),避免拷貝開(kāi)銷(xiāo)
        return id;
    }

    /**
     * @brief 注銷(xiāo)清理回調(diào)(支持重復(fù)調(diào)用,安全可重入)
     * @param id 注冊(cè)時(shí)返回的 ID
     */
    void unregisterCleanup(const int id) {
        QMutexLocker locker(&m_mutex);
        m_funcs.erase(id); // 不存在的 ID 無(wú)副作用
    }

    /**
     * @brief 執(zhí)行所有注冊(cè)的清理回調(diào)(程序退出時(shí)調(diào)用)
     * 特點(diǎn):拷貝回調(diào)列表后再執(zhí)行,避免回調(diào)中操作管理器導(dǎo)致死鎖
     */
    void cleanupAll() {
        std::map<int, std::function<void()>> copyFuncs;
        {
            // 局部作用域:釋放鎖后再執(zhí)行回調(diào),提高并發(fā)效率
            QMutexLocker locker(&m_mutex);
            copyFuncs = m_funcs; // 拷貝回調(diào)列表
            m_funcs.clear();     // 清空原列表,避免重復(fù)執(zhí)行
        }

        // 按注冊(cè)順序執(zhí)行回調(diào)(map 是有序容器,key 遞增)
        for (auto &[id, func] : copyFuncs) {
            if (func) {
                try {
                    func(); // 執(zhí)行清理邏輯
                } catch (...) {
                    // 捕獲所有異常,避免單個(gè)單例清理失敗影響其他
                    qWarning() << "[SingletonManager] Cleanup failed for id:" << id;
                }
            }
        }
    }

private:
    // 私有構(gòu)造/析構(gòu):禁止外部創(chuàng)建實(shí)例
    SingletonManager() = default;
    ~SingletonManager() = default;

    // 禁用拷貝/移動(dòng):確保管理器全局唯一
    Q_DISABLE_COPY_MOVE(SingletonManager)

    QMutex m_mutex;                          // 保護(hù)回調(diào)列表的線程安全
    std::map<int, std::function<void()>> m_funcs; // 存儲(chǔ)清理回調(diào)(有序)
    int m_nextId{1};                         // 回調(diào)注冊(cè) ID 生成器(從 1 開(kāi)始,0 為無(wú)效 ID)
};

#endif // SINGLETONMANAGER_H

設(shè)計(jì)亮點(diǎn):

  • 自身是單例: 靜態(tài)局部變量初始化(C++11 線程安全),無(wú)需額外加鎖;
  • 線程安全: 所有對(duì)回調(diào)列表的操作都通過(guò) QMutex 保護(hù);
  • 安全清理: 拷貝回調(diào)列表后釋放鎖,避免回調(diào)中調(diào)用 unregisterCleanup 導(dǎo)致死鎖;
  • 異常隔離: 單個(gè)單例清理失敗不影響其他,提高程序穩(wěn)定性;
  • 有序執(zhí)行: std::map 保證清理順序與注冊(cè)順序一致,解決單例依賴(lài)問(wèn)題。

3.2 單例模板類(lèi):Singleton.h

模板類(lèi)是單例框架的核心,通過(guò)泛型封裝通用邏輯,支持任意類(lèi)作為單例,無(wú)需修改目標(biāo)類(lèi)代碼。

#ifndef SINGLETON_H
#define SINGLETON_H

#include <QMutexLocker>
#include <QCoreApplication>
#include <QDebug>
#include <type_traits>
#include <utility> // 用于 std::forward

#include "SingletonManager.h"

// 靜態(tài)斷言:確保 T 是可構(gòu)造的(避免抽象類(lèi)作為單例)
template<typename T>
constexpr bool is_singleton_valid_v = std::is_constructible_v<T> && !std::is_abstract_v<T>;

// 單例模板類(lèi):支持帶參數(shù)構(gòu)造、自動(dòng)注冊(cè)清理、線程安全訪問(wèn)
template<typename T>
class Singleton {
    // 編譯期校驗(yàn):若 T 不可構(gòu)造或?yàn)槌橄箢?lèi),直接報(bào)錯(cuò)
    static_assert(is_singleton_valid_v<T>, 
                  "Singleton<T> requires T to be constructible and non-abstract");

public:
    /**
     * @brief 初始化單例(必須在調(diào)用 instance() 前執(zhí)行)
     * @tparam Args 構(gòu)造函數(shù)參數(shù)類(lèi)型
     * @param args 構(gòu)造函數(shù)參數(shù)(完美轉(zhuǎn)發(fā),支持左值/右值)
     * 特點(diǎn):自動(dòng)注冊(cè)清理回調(diào)到 SingletonManager,支持重復(fù)調(diào)用(僅首次有效)
     */
    template<typename... Args>
    static void init(Args &&... args) {
        QMutexLocker lockerInit(&mutex()); // 加鎖保證初始化線程安全
        if (instanceRef() != nullptr) {
            qWarning() << "[Singleton] " << typeid(T).name() << " has already been initialized";
            return;
        }

        // 完美轉(zhuǎn)發(fā)參數(shù),創(chuàng)建單例實(shí)例(支持任意構(gòu)造參數(shù))
        instanceRef() = new T(std::forward<Args>(args)...);

        // 注冊(cè)清理回調(diào)(僅首次初始化時(shí)注冊(cè))
        if (regIdRef() == 0) {
            regIdRef() = SingletonManager::instance().registerCleanup([] {
                QMutexLocker lockerCleanup(&mutex());
                delete instanceRef(); // 釋放單例實(shí)例
                instanceRef() = nullptr; // 重置指針,避免野指針
                qDebug() << "[Singleton] " << typeid(T).name() << " cleaned up";
            });
        }
    }

    /**
     * @brief 獲取單例實(shí)例指針(線程安全)
     * @return T* 單例指針(非空,調(diào)試模式下為空會(huì)觸發(fā)斷言)
     * 注意:必須先調(diào)用 init() 初始化,否則調(diào)試模式斷言失敗,release 模式可能崩潰
     */
    static T *instance() {
        QMutexLocker locker(&mutex());
        Q_ASSERT_X(instanceRef() != nullptr, 
                   "Singleton::instance()", 
                   qPrintable(QString("%1 not initialized! Call Singleton<%1>::init() first.").arg(typeid(T).name())));
        return instanceRef();
    }

    /**
     * @brief 手動(dòng)銷(xiāo)毀單例(主動(dòng)釋放資源)
     * 特點(diǎn):銷(xiāo)毀后可重新調(diào)用 init() 再次初始化,支持動(dòng)態(tài)啟停
     */
    static void shutdown() {
        QMutexLocker locker(&mutex());
        if (instanceRef() != nullptr) {
            delete instanceRef();
            instanceRef() = nullptr;
            qDebug() << "[Singleton] " << typeid(T).name() << " shut down manually";
        }

        // 注銷(xiāo)清理回調(diào)(避免重復(fù)銷(xiāo)毀)
        if (regIdRef() != 0) {
            SingletonManager::instance().unregisterCleanup(regIdRef());
            regIdRef() = 0;
        }
    }

    // 禁用默認(rèn)構(gòu)造/析構(gòu):禁止創(chuàng)建 Singleton 實(shí)例(僅通過(guò)靜態(tài)方法訪問(wèn))
    Singleton() = delete;
    ~Singleton() = delete;

    // 禁用拷貝/移動(dòng):確保單例唯一性
    Singleton(const Singleton &) = delete;
    Singleton &operator=(const Singleton &) = delete;
    Singleton(Singleton &&) = delete;
    Singleton &operator=(Singleton &&) = delete;

private:
    /**
     * @brief 獲取單例實(shí)例引用(靜態(tài)局部變量,懶加載)
     * 注意:靜態(tài)局部變量初始化線程安全(C++11 標(biāo)準(zhǔn))
     */
    static T *&instanceRef() {
        static T *inst = nullptr;
        return inst;
    }

    /**
     * @brief 獲取互斥鎖引用(靜態(tài)局部變量,懶加載)
     * 每個(gè)單例類(lèi)擁有獨(dú)立的互斥鎖,避免不同單例間鎖競(jìng)爭(zhēng)
     */
    static QMutex &mutex() {
        static QMutex m;
        return m;
    }

    /**
     * @brief 獲取清理回調(diào)注冊(cè) ID 引用
     * 用于跟蹤是否已注冊(cè)清理回調(diào),避免重復(fù)注冊(cè)
     */
    static int &regIdRef() {
        static int id = 0;
        return id;
    }
};

/**
 * @def GET_SINGLETON(Type)
 * @brief 獲取單例指針(可能為 nullptr,需自行判空)
 * 適用場(chǎng)景:允許單例未初始化的場(chǎng)景(如可選功能模塊)
 */
#define GET_SINGLETON(Type) (Singleton<Type>::instance())

/**
 * @def GET_SINGLETON_REF(Type)
 * @brief 獲取單例引用(調(diào)試模式下未初始化會(huì)觸發(fā)斷言)
 * 適用場(chǎng)景:確保單例必須存在的核心模塊(如配置管理)
 */
#define GET_SINGLETON_REF(Type) ([]() -> Type & { \
    Type *p = Singleton<Type>::instance();       \
    Q_ASSERT_X(p != nullptr, "GET_SINGLETON_REF", "Singleton not initialized"); \
    return *p;                                   \
}())

/**
 * @def GET_SINGLETON_OR(Type, alt)
 * @brief 獲取單例指針,若未初始化則返回替代值
 * 適用場(chǎng)景:需要降級(jí)策略的場(chǎng)景(如備用服務(wù))
 */
#define GET_SINGLETON_OR(Type, alt) (Singleton<Type>::instance() ? Singleton<Type>::instance() : (alt))

#endif // SINGLETON_H

設(shè)計(jì)亮點(diǎn):

  • 編譯期校驗(yàn): 通過(guò) static_assert 禁止抽象類(lèi)、不可構(gòu)造類(lèi)作為單例,提前暴露錯(cuò)誤;
  • 完美轉(zhuǎn)發(fā): std::forward 保留構(gòu)造參數(shù)的左值 / 右值屬性,支持任意構(gòu)造參數(shù)(包括臨時(shí)對(duì)象);
  • 獨(dú)立鎖機(jī)制: 每個(gè)單例類(lèi)擁有自己的 QMutex,避免不同單例間的鎖競(jìng)爭(zhēng),提高并發(fā)效率;
  • 靈活控制: 支持 init() 重復(fù)調(diào)用(冪等性)、shutdown() 手動(dòng)銷(xiāo)毀后重新初始化;
  • 安全宏定義: 提供三種訪問(wèn)宏,適配不同使用場(chǎng)景,調(diào)試模式下有明確斷言提示。

3.3 目標(biāo)單例類(lèi)示例:MyService.h

為了讓示例更完整,這里提供一個(gè)典型的 Qt 單例類(lèi)實(shí)現(xiàn)(支持信號(hào)槽、帶參數(shù)構(gòu)造):

#ifndef MYSERVICE_H
#define MYSERVICE_H

#include <QObject>
#include <QString>
#include <QDebug>

// 示例單例類(lèi):網(wǎng)絡(luò)服務(wù)管理(支持信號(hào)槽,帶參數(shù)構(gòu)造)
class MyService : public QObject {
    Q_OBJECT
public:
    /**
     * @brief 帶參數(shù)構(gòu)造函數(shù)(單例的構(gòu)造參數(shù)通過(guò) Singleton::init() 傳遞)
     * @param serverUrl 服務(wù)端地址
     * @param port 服務(wù)端口
     * @param parent 父對(duì)象(建議設(shè)為 nullptr,避免生命周期沖突)
     */
    explicit MyService(const QString &serverUrl, int port, QObject *parent = nullptr) 
        : QObject(parent), m_serverUrl(serverUrl), m_port(port) {
        qDebug() << "[MyService] Initialized with URL:" << serverUrl << ", port:" << port;
    }

    ~MyService() override {
        qDebug() << "[MyService] Destructor called";
    }

    // 示例業(yè)務(wù)方法
    void doSomething() {
        qDebug() << "[MyService] Doing something with" << m_serverUrl << ":" << m_port;
        // 實(shí)際業(yè)務(wù)邏輯:如網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)處理等
    }

signals:
    // 示例信號(hào):如服務(wù)狀態(tài)變化
    void serviceReady();

private:
    QString m_serverUrl; // 服務(wù)端地址
    int m_port;          // 服務(wù)端口
};

#endif // MYSERVICE_H

注意事項(xiàng):

  • 單例類(lèi)若繼承 QObject,建議將父對(duì)象設(shè)為 nullptr,避免 Qt 父子對(duì)象生命周期管理與單例沖突;
  • 構(gòu)造函數(shù)需為 publicprotected(若為 protected,需將 Singleton<T> 設(shè)為友元);
  • 避免在構(gòu)造函數(shù)中執(zhí)行耗時(shí)操作(如網(wǎng)絡(luò)連接),可提供 initService() 等方法延遲初始化。

3.4 完整使用示例(main 函數(shù))

#include <QCoreApplication>
#include <QTimer>
#include "SingletonManager.h"
#include "Singleton.h"
#include "MyService.h"

int main(int argc, char *argv[]) {
    // 1. 必須先創(chuàng)建 QCoreApplication(QMutex、QObject 等依賴(lài) Qt 環(huán)境初始化)
    QCoreApplication app(argc, argv);

    // 2. 連接 aboutToQuit 信號(hào),程序退出時(shí)統(tǒng)一清理單例
    QObject::connect(&app, &QCoreApplication::aboutToQuit, []() {
        qDebug() << "\n[Main] Starting singleton cleanup...";
        SingletonManager::instance().cleanupAll();
        qDebug() << "[Main] Singleton cleanup finished";
    });

    // 3. 初始化單例(傳遞構(gòu)造參數(shù),自動(dòng)注冊(cè)清理回調(diào))
    Singleton<MyService>::init("http://example.com", 8080);

    // 4. 三種訪問(wèn)單例的方式
    // 方式1:獲取指針(需手動(dòng)判空,適合可選模塊)
    MyService *svcPtr = GET_SINGLETON(MyService);
    if (svcPtr) {
        svcPtr->doSomething();
    }

    // 方式2:獲取引用(調(diào)試模式斷言非空,適合核心模塊)
    MyService &svcRef = GET_SINGLETON_REF(MyService);
    svcRef.doSomething();

    // 方式3:獲取指針或替代值(適合降級(jí)策略)
    MyService *svcOr = GET_SINGLETON_OR(MyService, nullptr);
    if (svcOr) {
        svcOr->doSomething();
    }

    // 5. 示例:手動(dòng)銷(xiāo)毀單例(可選)
    QTimer::singleShot(2000, []() {
        qDebug() << "\n[Main] Shutting down MyService manually...";
        Singleton<MyService>::shutdown();

        // 銷(xiāo)毀后可重新初始化
        qDebug() << "[Main] Reinitializing MyService...";
        Singleton<MyService>::init("http://new-example.com", 9090);
        GET_SINGLETON_REF(MyService).doSomething();
    });

    // 6. 3秒后退出程序(觸發(fā) aboutToQuit 清理)
    QTimer::singleShot(3000, &app, &QCoreApplication::quit);

    return app.exec();
}

運(yùn)行輸出:

[MyService] Initialized with URL: "http://example.com" , port: 8080
[MyService] Doing something with "http://example.com" : 8080
[MyService] Doing something with "http://example.com" : 8080
[MyService] Doing something with "http://example.com" : 8080

[Main] Shutting down MyService manually...
[MyService] Destructor called
[Singleton] MyService cleaned up
[Main] Reinitializing MyService...
[MyService] Initialized with URL: "http://new-example.com" , port: 9090
[MyService] Doing something with "http://new-example.com" : 9090

[Main] Starting singleton cleanup...
[MyService] Destructor called
[Singleton] MyService cleaned up
[Main] Singleton cleanup finished

四、核心注意事項(xiàng)

4.1 初始化順序

  • 必須先創(chuàng)建 QCoreApplication QMutex、QObject 等 Qt 組件依賴(lài) Qt 環(huán)境初始化,因此 QCoreApplication 必須在 Singleton::init() 之前創(chuàng)建;
  • 單例依賴(lài)順序: 若單例 A 依賴(lài)單例 B,需先初始化 B 再初始化 A(清理順序與注冊(cè)順序一致,即先清理 A 再清理 B,避免依賴(lài)失效)。

4.2 線程安全

  • 初始化線程安全: init() 方法通過(guò) QMutex 保護(hù),多線程并發(fā)調(diào)用僅首次初始化有效;
  • 訪問(wèn)線程安全: instance() 方法通過(guò) QMutex 保護(hù),避免多線程同時(shí)訪問(wèn)未初始化的實(shí)例;
  • 單例內(nèi)部線程安全: 本文框架僅保證實(shí)例創(chuàng)建 / 銷(xiāo)毀的線程安全,單例類(lèi)自身的成員函數(shù)需根據(jù)業(yè)務(wù)需求添加鎖(如 QMutex)。

4.3 禁止拷貝移動(dòng)

  • 單例類(lèi)必須禁用拷貝構(gòu)造、賦值運(yùn)算符(通過(guò) Q_DISABLE_COPY_MOVE 或手動(dòng)刪除),否則可能通過(guò)拷貝創(chuàng)建多個(gè)實(shí)例;
  • Singleton<T> 模板已禁用拷貝移動(dòng),目標(biāo)單例類(lèi)需自行禁用(如示例 MyService 雖未顯式禁用,但繼承 QObject 后自動(dòng)禁用)。

4.4 內(nèi)存泄漏防護(hù)

  • 必須連接 QCoreApplication::aboutToQuitSingletonManager::cleanupAll(),否則程序異常退出時(shí)可能導(dǎo)致內(nèi)存泄漏;
  • 若單例類(lèi)繼承 QObject,禁止將其設(shè)為其他 QObject 的子對(duì)象(否則 Qt 可能自動(dòng)銷(xiāo)毀實(shí)例,導(dǎo)致二次釋放)。

4.5 調(diào)試與 Release 模式差異

  • 調(diào)試模式(Debug):instance()、GET_SINGLETON_REF 會(huì)觸發(fā)斷言,快速定位未初始化的錯(cuò)誤;
  • 發(fā)布模式(Release):斷言失效,instance() 可能返回 nullptr,需自行判空(建議核心模塊使用 GET_SINGLETON_REF,非核心模塊使用 GET_SINGLETON 并判空)。

五、進(jìn)階用法

5.1 懶加載單例(無(wú)需手動(dòng) init)

默認(rèn)實(shí)現(xiàn)需要手動(dòng)調(diào)用 init(),若需懶加載(首次調(diào)用 instance() 時(shí)自動(dòng)初始化),可修改 instance() 方法:

static T *instance() {
    QMutexLocker locker(&mutex());
    if (instanceRef() == nullptr) {
        // 無(wú)參構(gòu)造(若需帶參數(shù),需調(diào)整設(shè)計(jì),如全局配置)
        instanceRef() = new T();
        // 自動(dòng)注冊(cè)清理回調(diào)
        if (regIdRef() == 0) {
            regIdRef() = SingletonManager::instance().registerCleanup([] {
                QMutexLocker locker(&mutex());
                delete instanceRef();
                instanceRef() = nullptr;
            });
        }
    }
    return instanceRef();
}

注意: 懶加載模式僅支持無(wú)參構(gòu)造,若需帶參數(shù),需通過(guò)全局配置或其他方式傳遞參數(shù)。

5.2 單例銷(xiāo)毀優(yōu)先級(jí)

若需控制單例的銷(xiāo)毀順序(如先銷(xiāo)毀依賴(lài)方,再銷(xiāo)毀被依賴(lài)方),可擴(kuò)展 SingletonManager 支持優(yōu)先級(jí):

// 修改 SingletonManager 的注冊(cè)接口,增加優(yōu)先級(jí)參數(shù)
int registerCleanup(std::function<void()> fn, int priority = 0) {
    QMutexLocker locker(&m_mutex);
    int id = m_nextId++;
    // 用 pair<priority, id> 作為 key,按優(yōu)先級(jí)降序排序(優(yōu)先級(jí)高的先清理)
    m_funcs.emplace(std::make_pair(-priority, id), std::move(fn));
    return id;
}

// 對(duì)應(yīng)的 map 類(lèi)型修改為:
std::map<std::pair<int, int>, std::function<void()>> m_funcs;

使用時(shí)指定優(yōu)先級(jí):

// 高優(yōu)先級(jí)(先清理)
Singleton<MyService>::initWithPriority(10, "http://example.com", 8080);

5.3 線程局部單例(TLS)

若需每個(gè)線程擁有獨(dú)立的單例實(shí)例(如線程局部緩存),可修改 instanceRef() 為線程局部變量:

static T *&instanceRef() {
    thread_local T *inst = nullptr; // 線程局部變量,每個(gè)線程獨(dú)立
    return inst;
}

static QMutex &mutex() {
    thread_local QMutex m; // 每個(gè)線程獨(dú)立的鎖
    return m;
}

注意:線程局部單例的清理需在線程退出時(shí)手動(dòng)調(diào)用 shutdown(),避免線程泄漏。

六、常見(jiàn)問(wèn)題排查

6.1 斷言失?。篠ingleton not initialized

  • 原因:未調(diào)用 Singleton::init() 就調(diào)用 instance()GET_SINGLETON_REF
  • 解決:確保 init()instance() 之前調(diào)用,且 QCoreApplication 已創(chuàng)建。

6.2 內(nèi)存泄漏(Valgrind 檢測(cè)到泄漏)

  • 原因:未連接 QCoreApplication::aboutToQuitSingletonManager::cleanupAll();
  • 解決:在 main 函數(shù)中添加連接代碼。

6.3 二次釋放崩潰

  • 原因:?jiǎn)卫?lèi)被 Qt 父子對(duì)象管理(如設(shè)置了父對(duì)象),導(dǎo)致 Qt 自動(dòng)銷(xiāo)毀后,SingletonManager 再次銷(xiāo)毀;
  • 解決:?jiǎn)卫?lèi)構(gòu)造時(shí)父對(duì)象設(shè)為 nullptr,禁止將單例設(shè)為其他 QObject 的子對(duì)象。

6.4 多線程并發(fā)初始化導(dǎo)致崩潰

  • 原因:使用了 C++11 之前的編譯器(靜態(tài)局部變量初始化非線程安全);
  • 解決:升級(jí)編譯器到支持 C++11 及以上標(biāo)準(zhǔn),或手動(dòng)為 instanceRef() 加鎖。

七、框架優(yōu)勢(shì)總結(jié)

本文實(shí)現(xiàn)的單例框架相比傳統(tǒng)單例(如餓漢式、懶漢式),具有以下優(yōu)勢(shì):

  • 通用性強(qiáng): 模板化設(shè)計(jì),支持任意可構(gòu)造類(lèi),無(wú)需修改目標(biāo)類(lèi)代碼;
  • 線程安全: 基于 QMutex 實(shí)現(xiàn)初始化 / 訪問(wèn)安全,兼容 Qt 多線程環(huán)境;
  • 自動(dòng)清理: 通過(guò) SingletonManager 統(tǒng)一管理,避免內(nèi)存泄漏;
  • 靈活構(gòu)造: 支持帶參數(shù)構(gòu)造(完美轉(zhuǎn)發(fā)),適配復(fù)雜單例類(lèi);
  • 安全易用: 提供斷言校驗(yàn)、宏定義快捷訪問(wèn),降低使用成本;
  • 可擴(kuò)展: 支持手動(dòng)銷(xiāo)毀、優(yōu)先級(jí)清理、線程局部單例等進(jìn)階需求。

該框架可直接用于 Qt 控制臺(tái)程序、桌面程序(QWidget)、移動(dòng)程序(Qt Quick)等所有場(chǎng)景,是 Qt 開(kāi)發(fā)中單例模式的優(yōu)選實(shí)現(xiàn)方案。

以上就是Qt優(yōu)雅實(shí)現(xiàn)線程安全單例模式(模板化和自動(dòng)清理)的詳細(xì)內(nèi)容,更多關(guān)于Qt線程單例模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語(yǔ)言數(shù)據(jù)的存儲(chǔ)專(zhuān)項(xiàng)分析

    C語(yǔ)言數(shù)據(jù)的存儲(chǔ)專(zhuān)項(xiàng)分析

    使用編程語(yǔ)言進(jìn)行編程時(shí),需要用到各種變量來(lái)存儲(chǔ)各種信息。變量保留的是它所存儲(chǔ)的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個(gè)變量時(shí),就會(huì)在內(nèi)存中保留一些空間。您可能需要存儲(chǔ)各種數(shù)據(jù)類(lèi)型的信息,操作系統(tǒng)會(huì)根據(jù)變量的數(shù)據(jù)類(lèi)型,來(lái)分配內(nèi)存和決定在保留內(nèi)存中存儲(chǔ)什么
    2022-07-07
  • C語(yǔ)言運(yùn)用函數(shù)指針數(shù)組實(shí)現(xiàn)計(jì)算器功能

    C語(yǔ)言運(yùn)用函數(shù)指針數(shù)組實(shí)現(xiàn)計(jì)算器功能

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言運(yùn)用函數(shù)指針數(shù)組實(shí)現(xiàn)計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • C/C++利用篩選法算素?cái)?shù)的方法示例

    C/C++利用篩選法算素?cái)?shù)的方法示例

    這篇文章主要給大家介紹了關(guān)于利用C/C++篩選法算素?cái)?shù)的相關(guān)資料,文中給大家列舉了普通枚舉法和篩選法兩種方法實(shí)現(xiàn)的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • OpenCV透視變換應(yīng)用之書(shū)本視圖矯正+廣告屏幕切換

    OpenCV透視變換應(yīng)用之書(shū)本視圖矯正+廣告屏幕切換

    透視變換是指利用透視中心、像點(diǎn)、目標(biāo)點(diǎn)三點(diǎn)共線的條件,按透視旋轉(zhuǎn)定律使承影面繞跡線旋轉(zhuǎn)某一角度,破壞原有的投影光線束,仍能保持承影面上投影幾何圖形不變的變換。本文將為大家介紹兩個(gè)OpenCV透視變換應(yīng)用,需要的可以參考一下
    2022-08-08
  • 詳解C語(yǔ)言中的字符串?dāng)?shù)組

    詳解C語(yǔ)言中的字符串?dāng)?shù)組

    這篇文章主要介紹了C語(yǔ)言中的字符串?dāng)?shù)組,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • C語(yǔ)言簡(jiǎn)明清晰講解枚舉

    C語(yǔ)言簡(jiǎn)明清晰講解枚舉

    枚舉法的本質(zhì)就是從所有候選答案中去搜索正確的解,枚舉算法簡(jiǎn)單粗暴,他暴力的枚舉所有可能,盡可能地嘗試所有的方法,感興趣的朋友來(lái)看看吧
    2022-05-05
  • 基于C語(yǔ)言打造高效通訊錄的示例代碼

    基于C語(yǔ)言打造高效通訊錄的示例代碼

    本文主要介紹了如何使用C語(yǔ)言實(shí)現(xiàn)一個(gè)通訊錄。實(shí)現(xiàn)通訊錄的過(guò)程中,會(huì)大量用到C語(yǔ)言的知識(shí)點(diǎn),包括但不限于:函數(shù)、自定義類(lèi)型、指針、動(dòng)態(tài)內(nèi)存管理、文件操作,感興趣的可以了解一下
    2023-05-05
  • C++輕量級(jí)UI庫(kù)DuiLib使用指南與優(yōu)劣詳解

    C++輕量級(jí)UI庫(kù)DuiLib使用指南與優(yōu)劣詳解

    DuiLib(及其增強(qiáng)版DuiLib_Ultimate)是專(zhuān)為Windows優(yōu)化的輕量級(jí)UI庫(kù),具有界面靈活、自定義性強(qiáng)等特點(diǎn),本文給大家介紹C++輕量級(jí)UI庫(kù)DuiLib使用指南與優(yōu)劣解析,感興趣的朋友跟隨小編一起看看吧
    2026-05-05
  • C語(yǔ)言輪轉(zhuǎn)數(shù)組的三種實(shí)現(xiàn)

    C語(yǔ)言輪轉(zhuǎn)數(shù)組的三種實(shí)現(xiàn)

    輪轉(zhuǎn)數(shù)組是一種將數(shù)組元素循環(huán)移動(dòng)的處理方式,它通常用于解決一些需要對(duì)固定長(zhǎng)度的數(shù)組進(jìn)行循環(huán)滾動(dòng)處理的問(wèn)題,本文就介紹了C語(yǔ)言輪轉(zhuǎn)數(shù)組的三種實(shí)現(xiàn),感興趣的可以了解一下
    2023-08-08
  • C++編譯期字符串處理方法

    C++編譯期字符串處理方法

    這篇文章給大家介紹了C++編譯期字符串處理方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2026-03-03

最新評(píng)論

泾源县| 滨州市| 盘山县| 武夷山市| 汝城县| 进贤县| 衡水市| 门源| 屏东县| 花莲县| 沁阳市| 苏尼特左旗| 贡觉县| 贵港市| 文水县| 寿光市| 吉林省| 长沙市| 安图县| 丰顺县| 汉阴县| 邢台市| 灌云县| 连江县| 韩城市| 贡山| 梁山县| 潢川县| 江华| 信丰县| 上饶县| 平昌县| 丰都县| 盐池县| 洛扎县| 长顺县| 蒲江县| 龙口市| 仙居县| 临颍县| 绥滨县|