C++单例模式实现线程安全日志输出详解

唐可
唐可
发布时间:2026-10-15 | 6人浏览过
原创

本文主要介绍如何在 C++ 中利用单例模式实现一个线程安全的日志输出工具,包括关键代码实现与注意事项。

一、单例模式的背景与应用场景

单例模式是一种创建型设计模式,其核心目标是确保一个类仅有一个实例,并提供一个全局访问点供外界调用。这种模式常用于资源管理,如数据库连接池或配置信息管理器,以避免因多次创建对象导致的资源浪费和数据不一致。

常见应用场景

  • 日志记录器:应用程序通常只需维护一个统一的日志记录入口,确保所有模块的日志能准确输出到同一位置。
  • 配置管理器:全局唯一的配置对象可防止不同模块读取到不一致的配置数据。
  • 数据库连接池:控制连接对象的创建数量,优化系统资源。

在实现单例时,基础做法是将构造函数设为私有(Private),从而阻止外部直接实例化该类。同时提供一个公有静态方法获取唯一实例。

二、C++ 基础单例实现分析

以下代码展示了最基础的单例写法:通过指针判断是否为空来决定是否新建对象。

class Singleton {
public:
  static Singleton* GetInstance() {
    if (instance_ == nullptr) {
      instance_ = new Singleton();
    }
    return instance_;
  }
 
  void DoSomething() {
    // ...
  }
 
private:
  Singleton() {
    // ...
  }
 
  static Singleton* instance_;
};
 
Singleton* Singleton::instance_ = nullptr;

虽然上述方式实现了“单一实例”的功能,但在多线程环境下存在隐患。如果两个线程几乎同时执行 `if (instance_ == nullptr)` 语句,可能会因为时间差导致多个线程同时进入分支,从而创建出多个实例,破坏了单例模式的初衷。

三、基于单例模式的日志输出完整实现

为了实现可靠的日志功能,我们需要结合文件操作接口,并使用同步机制保证线程安全。

1. 核心代码结构说明

  • 私有构造与析构:限制类的实例化范围。
  • 文件流管理:使用 std::ofstream 以 app(追加)模式打开日志文件。
  • 获取实例:采用“双重检查锁定”机制进行优化。

2. 完整代码参考

以下是包含基本错误处理和线程保护的单例日志记录器实现(注:实际生产环境中建议使用标准库提供的信号量等锁机制):

#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <mutex>
using namespace std;

class Logger {
private:
    static Logger* instance;
    ofstream logFile;
    mutex mtx; // 用于保护临界区
 
    Logger() {
        string filename = "log.txt";
        logFile.open(filename.c_str(), ios::out | ios::app);
        if (!logFile.is_open()) {
            cerr << "Failed to open log file!" << endl;
        }
    }
 
public:
    // 获取实例,注意此处需加锁以保证线程安全
    static Logger* getInstance() {
        if (instance == NULL) {
            lock_guard<mutex> guard(mtx);
            if (instance == NULL) { // 双重检查
                instance = new Logger();
            }
        }
        return instance;
    }
 
    void log(const string& message) {
        lock_guard<mutex> guard(mtx); // 每次写文件前上锁
        if (logFile.is_open()) {
            time_t now = time(0);
            char* dt = ctime(&now);
            // 去除 ctime 自带换行符影响
            size_t len = strlen(dt);
            if (len > 0 && dt[len-1] == '\n') dt[len-1] = '\\0';
            logFile << dt << ": " << message << endl;
            logFile.flush(); // 确保数据立即落盘
        }
    }
};

Logger* Logger::instance = NULL;

3. 测试调用

在主函数中,通过 getInstance() 获取日志对象即可开始记录消息:

int main() {
    Logger* logger = Logger::getInstance();
    logger->log("Hello World!");
    logger->log("Test complete.");
    return 0;
}

程序运行后,会在当前目录下生成 log.txt 文件,其中按顺序记录了带有时间戳的信息。由于使用了锁,即使是高并发多线程调用,也不会出现交叉或丢失日志内容的情况。

四、总结

利用单例模式是实现全局统一日志管理的经典方案。在实际开发中,务必注意两点:一是必须将构造函数私有化以防止随意复制;二是若应用在多线程环境下,必须引入互斥锁或原子操作来保障初始化和写入过程的安全性。以上就是C++实现单例模式日志输出的详细内容,更多关于C++设计模式的应用资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表