C++编程单例模式详细解释---模拟一个网络配置管理器,负责管理和分发网络连接参数

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供全局访问点。以下是单例模式常见的应用场景,结合实际案例进行说明:

一、系统资源管理类

1. 日志记录器(Logger)
  • 场景:在程序中需要全局统一的日志输出,避免多个日志实例同时写入文件导致数据混乱。
  • 案例
    例如 Linux 系统的syslog服务、Java 中的Log4j框架,通过单例模式确保整个系统只有一个日志实例,统一管理日志级别、输出格式和目标位置。
  • 优势
    保证日志的一致性和线程安全,避免重复创建日志对象消耗资源。
2. 配置管理器(Config Manager)
  • 场景:读取和管理程序的配置文件(如数据库连接参数、系统参数)。
  • 案例
    嵌入式系统中读取config.ini文件,或 Web 应用中读取application.properties配置。
  • 优势
    全局共享配置数据,修改配置后所有模块实时生效,避免重复读取文件开销。
3. 数据库连接池(Database Connection Pool)
  • 场景:管理数据库连接的创建、复用和释放,避免频繁创建连接导致性能损耗。
  • 案例
    C++ 中的Pqxx库、Java 的HikariCP连接池,通过单例模式确保全局唯一的连接池实例。
  • 优势
    控制连接数量,减少资源消耗,提升数据库操作效率。

二、全局状态管理类

1. 任务调度器(Task Scheduler)
  • 场景:管理程序中的定时任务(如定时备份、周期性数据同步)。
  • 案例
    Linux 的crontab、Python 的APScheduler,通过单例模式统一管理所有任务的调度逻辑。
  • 优势
    避免多实例调度导致任务重复执行,确保时序控制的一致性。
2. 状态管理器(State Manager)
  • 场景:维护程序的全局状态(如用户登录状态、游戏进度)。
  • 案例
    游戏引擎中的GameState类,管理游戏的 "开始""暂停""结束" 等状态。
  • 优势
    各模块可直接访问全局状态,简化状态同步逻辑。

三、资源控制类

1. 打印机池(Printer Pool)
  • 场景:管理共享打印机的访问,避免多进程同时打印导致冲突。
  • 案例
    操作系统的打印服务(如 Windows 的 "打印机" 管理),通过单例模式统一调度打印任务。
  • 优势
    控制打印队列顺序,防止硬件资源竞争。
2. 线程池(Thread Pool)
  • 场景:管理线程的创建和复用,避免频繁创建线程导致系统开销。
  • 案例
    C++11 的std::thread结合单例模式实现全局线程池,或 Java 的ThreadPoolExecutor
  • 优势
    控制线程数量,减少上下文切换损耗,提升并发性能。

四、工具类与服务类

1. 网络连接管理器(Network Manager)
  • 场景:管理网络连接的建立、断开和重连(如 HTTP 客户端、Socket 连接)。
  • 案例
    安卓系统的ConnectivityManager,统一管理设备的网络连接状态。
  • 优势
    避免多实例同时操作网络导致资源浪费,简化网络请求逻辑。
2. 缓存管理器(Cache Manager)
  • 场景:管理数据缓存(如内存缓存、磁盘缓存),提高数据读取效率。
  • 案例
    Redis 客户端库通过单例模式维护全局缓存连接,加速数据存取。
  • 优势
    统一缓存策略(如过期时间、淘汰算法),避免缓存不一致问题。

五、框架与库中的应用

1. 设计框架核心类
  • 案例
    • Java 的java.lang.Runtime类,提供对 JVM 运行时环境的访问。
    • Qt 框架中的QApplication类,管理 GUI 应用的主事件循环。
  • 优势
    框架核心功能全局唯一,确保程序逻辑的一致性。
2. 资源加载器(Resource Loader)
  • 场景:加载图片、音频、配置文件等资源,避免重复加载。
  • 案例
    游戏引擎中的ResourceManager,通过单例模式缓存已加载的资源。
  • 优势
    减少内存占用,提升资源加载速度。

六、单例模式的适用场景总结

  1. 需要全局唯一实例:如日志、配置、状态管理等,避免多实例导致数据混乱。
  2. 资源密集型对象:如数据库连接池、线程池,减少创建和销毁开销。
  3. 控制资源访问:如打印机、网络连接,避免并发访问冲突。
  4. 简化全局访问:提供统一入口,降低模块间的耦合度。

注意事项

  • 线程安全 :单例模式需确保多线程环境下实例唯一(如 C++ 通过std::mutex加锁,Java 通过volatile和双重检查锁定)。
  • 依赖管理:单例可能导致 "隐式依赖",增加测试难度(可通过接口隔离或依赖注入缓解)。
  • 内存泄漏:长期存活的单例可能持有资源不释放,需谨慎设计销毁逻辑。

通过合理应用单例模式,可在保证全局一致性的同时,优化资源管理和系统性能。

七、单例模式示例

使用 C++ 实现一个包含数据传输和使用的单例模式示例,该示例模拟一个网络配置管理器,负责管理和分发网络连接参数。

network_config_manager.h文件
cpp 复制代码
#pragma once
#include <string>
#include <mutex>
#include <memory>

class NetworkConfigManager {
public:
    // 获取单例实例的静态方法
    static NetworkConfigManager& getInstance();
    
    // 禁用拷贝构造和赋值运算符
    NetworkConfigManager(const NetworkConfigManager&) = delete;
    NetworkConfigManager& operator=(const NetworkConfigManager&) = delete;
    
    // 设置网络配置
    void setConfig(const std::string& server, int port, const std::string& apiKey);
    
    // 获取网络配置
    std::string getServer() const;
    int getPort() const;
    std::string getApiKey() const;
    
    // 模拟网络请求
    void sendData(const std::string& data) const;

private:
    // 私有构造函数
    NetworkConfigManager() = default;
    
    // 网络配置数据
    std::string m_server;
    int m_port;
    std::string m_apiKey;
    
    // 线程安全的互斥锁
    mutable std::mutex m_mutex;
};    
network_config_manager.cpp文件
cpp 复制代码
#include "network_config_manager.h"
#include <iostream>
#include <thread>
#include <chrono>

// 静态成员变量初始化
NetworkConfigManager& NetworkConfigManager::getInstance() {
    static NetworkConfigManager instance;
    return instance;
}

void NetworkConfigManager::setConfig(const std::string& server, int port, const std::string& apiKey) {
    std::lock_guard<std::mutex> lock(m_mutex);
    std::cout << "[" << std::this_thread::get_id() << "] 设置网络配置: " 
              << server << ":" << port << std::endl;
    m_server = server;
    m_port = port;
    m_apiKey = apiKey;
}

std::string NetworkConfigManager::getServer() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_server;
}

int NetworkConfigManager::getPort() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_port;
}

std::string NetworkConfigManager::getApiKey() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_apiKey;
}

void NetworkConfigManager::sendData(const std::string& data) const {
    std::lock_guard<std::mutex> lock(m_mutex);
    std::cout << "[" << std::this_thread::get_id() << "] 发送数据到 " 
              << m_server << ":" << m_port << " (API Key: " << m_apiKey << ")" << std::endl;
    std::cout << "数据内容: " << data << std::endl;
    
    // 模拟网络请求延迟
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
}    
main.cpp文件
cpp 复制代码
#include "network_config_manager.h"
#include <thread>
#include <vector>
#include <iostream>

// 模拟数据生产者线程函数
void producerThread() {
    // 获取单例实例并设置配置
    NetworkConfigManager::getInstance().setConfig(
        "api.example.com", 443, "SECURE_API_KEY_12345");
    
    // 模拟发送数据
    for (int i = 0; i < 3; ++i) {
        NetworkConfigManager::getInstance().sendData(
            "Producer Data Packet #" + std::to_string(i));
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }
}

// 模拟数据消费者线程函数
void consumerThread(int id) {
    // 等待配置设置完成
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    
    // 使用单例实例获取配置并发送数据
    for (int i = 0; i < 2; ++i) {
        NetworkConfigManager::getInstance().sendData(
            "Consumer " + std::to_string(id) + " Data #" + std::to_string(i));
        std::this_thread::sleep_for(std::chrono::milliseconds(300));
    }
}

int main() {
    std::cout << "=== 单例模式示例:网络配置管理器 ===" << std::endl;
    
    // 创建多个线程模拟并发环境
    std::vector<std::thread> threads;
    
    // 创建生产者线程
    threads.emplace_back(producerThread);
    
    // 创建3个消费者线程
    for (int i = 1; i <= 3; ++i) {
        threads.emplace_back(consumerThread, i);
    }
    
    // 等待所有线程完成
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << "=== 程序执行完毕 ===" << std::endl;
    return 0;
}    

这个示例展示了一个典型的单例模式实现,包含以下关键点:

  1. 单例实现

    • 使用 C++11 的静态局部变量方式实现线程安全的懒汉式单例
    • 禁用拷贝构造和赋值运算符,防止实例被复制
    • 通过私有构造函数确保无法从外部直接创建实例
  2. 数据传输和使用

    • 提供setConfig()方法设置网络连接参数
    • 提供sendData()方法模拟网络数据发送
    • 所有线程共享同一实例的数据
  3. 线程安全

    • 使用std::mutex保护共享数据
    • 所有读写操作都在锁的保护下进行
  4. 示例运行流程

    • 一个生产者线程设置网络配置并发送数据
    • 多个消费者线程使用相同配置发送各自的数据
    • 所有线程通过单例实例访问和共享配置数据

这个示例展示了单例模式在实际应用中的典型用法,特别是在需要全局共享资源和配置的场景中。

这部分代码如果需要测试,可以根据下面流程进行操作:

八、单例模式示例linux运行方法

1. 创建文件

打开linux终端,将上例中的三个文件(network_config_manager.hnetwork_config_manager.cppmain.cpp)保存到同一目录下。

输入第一行,点击回车,可以创建一个singleton_example文件目录。

输入第二行,点击回车,可以在该目录下,创建这三个文件。

bash 复制代码
mkdir singleton_example && cd singleton_example
touch network_config_manager.h network_config_manager.cpp main.cpp
# 将代码分别复制到对应的文件中
2. 文件添加代码

使用vi指令,若是有不清楚该指令使用方法的,可以在这个连接下进行查看学习vim以及vi编辑器常用快捷键指令-CSDN博客

打开文件之后,直接将上面代码复制到文件中,最后使用 ":x",保存并退出vi编辑模式

3. 编译代码

使用 g++ 编译器将三个文件编译为可执行文件:

cpp 复制代码
g++ -std=c++11 -pthread network_config_manager.cpp main.cpp -o singleton_demo
4. 运行程序

编译完成之后,可以得到一个demo,使用下方指令运行这个demo即可

cpp 复制代码
./singleton_demo
5. 输出结果

运行结果如下图所示

九、代码具体分析

1. 线程创建逻辑

main()函数中:

cpp 复制代码
int main() {
    // 创建生产者线程
    threads.emplace_back(producerThread);
    
    // 创建3个消费者线程
    for (int i = 1; i <= 3; ++i) {
        threads.emplace_back(consumerThread, i);
    }
    // ...
}
  • producerThread:1 个生产者线程。
  • consumerThread:通过循环创建 3 个消费者线程(i=1,2,3)。
  • 主线程(main函数本身)也会参与执行。
2. 总线程数计算
  • 生产者线程:1 个
  • 消费者线程:3 个
  • 主线程:1 个(程序启动时默认存在)

总计5 个线程

3. 线程执行流程
  1. 主线程:创建生产者和消费者线程,然后等待所有线程完成。
  2. 生产者线程:设置网络配置并发送 3 次数据。
  3. 消费者线程(3 个):等待配置完成后,各发送 2 次数据。

该示例中创建了4 个子线程(1 生产者 + 3 消费者) ,加上主线程 ,总共有5 个线程并发执行。单例模式通过互斥锁确保所有线程安全访问共享的配置数据。

相关推荐
青衫码上行4 小时前
【MySQL数据库 | 第五篇】DDL操作2
数据库·mysql
远方16094 小时前
43-Oracle 系统视图一览
数据库·sql·oracle·database
我最厉害。,。5 小时前
C2远控篇&C&C++&ShellCode分离&File提取&Http协议&Argv参数&Sock管道
c语言·c++·http
Cyrus_柯5 小时前
C++(面向对象编程——关键字)
开发语言·c++·算法·面向对象
2013编程爱好者5 小时前
C++二分查找
开发语言·c++·算法·二分查找
叶子椰汁5 小时前
ORMPP链接MySQL 8.0错误
服务器·数据库·c++·mysql
电商数据girl5 小时前
【经验分享】浅谈京东商品SKU接口的技术实现原理
java·开发语言·前端·数据库·经验分享·eclipse·json
老兵发新帖6 小时前
AWS RDS :多引擎托管数据库服务
数据库·云计算·aws
十五年专注C++开发6 小时前
QSimpleUpdater:解锁 Qt 应用自动更新的全新姿势
开发语言·c++·qt
liyongjie6 小时前
openEuler安装BenchmarkSQL
数据库