如何优雅的实现C++的单例模式,直接上代码!

如何优雅的实现C++的单例模式,直接上代码!

编码文章call10242025-04-09 11:26:2525A+A-


几天不多说,直接来点实用的,其实,在 C++11 中,可以使用
std::call_once 结合 std::once_flag 实现线程安全的单例模式。以下是一个结合单例模式与线程池的实现示例:


实现代码

 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 class ThreadPool {
 public:
     // 获取单例实例
     static ThreadPool& getInstance() {
         std::call_once(initFlag_, []() {
             instance_.reset(new ThreadPool());
         });
         return *instance_;
     }
 
     // 添加任务到线程池
     template
     void enqueue(F&& task) {
         {
             std::unique_lock lock(queueMutex_);
             tasks_.emplace(std::forward(task));
         }
         condition_.notify_one();
     }
 
     // 禁止拷贝和赋值
     ThreadPool(const ThreadPool&) = delete;
     ThreadPool& operator=(const ThreadPool&) = delete;
 
 private:
     ThreadPool(size_t numThreads = std::thread::hardware_concurrency()) 
         : stop_(false) {
         for (size_t i = 0; i < numThreads; ++i) {
             workers_.emplace_back([this] {
                 while (true) {
                     std::function task;
                     {
                         std::unique_lock lock(queueMutex_);
                         condition_.wait(lock, [this] {
                             return stop_ || !tasks_.empty();
                         });
 
                         if (stop_ && tasks_.empty()) return;
 
                         task = std::move(tasks_.front());
                         tasks_.pop();
                     }
                     task();
                 }
             });
         }
     }
 
     ~ThreadPool() {
         {
             std::unique_lock lock(queueMutex_);
             stop_ = true;
         }
         condition_.notify_all();
         for (auto& worker : workers_) {
             worker.join();
         }
     }
 
     // 单例相关成员
     static std::unique_ptr instance_;
     static std::once_flag initFlag_;
 
     // 线程池成员
     std::vector workers_;
     std::queue<std::function> tasks_;
     std::mutex queueMutex_;
     std::condition_variable condition_;
     bool stop_;
 };
 
 // 初始化静态成员
 std::unique_ptr ThreadPool::instance_ = nullptr;
 std::once_flag ThreadPool::initFlag_;
 
 // 示例使用
 int main() {
     // 获取单例线程池
     ThreadPool& pool = ThreadPool::getInstance();
 
     // 添加任务
     for (int i = 0; i < 10; ++i) {
         pool.enqueue([i] {
             std::cout << "Task " << i << " executed by thread "
                       << std::this_thread::get_id() << std::endl;
         });
     }
 
     // 主线程等待任务完成(实际应用中需更严谨的同步)
     std::this_thread::sleep_for(std::chrono::seconds(1));
     return 0;
 }

关键点解释

1. 单例模式实现

  • std::call_once + std::once_flag: 通过 std::call_once 确保单例的初始化代码(new ThreadPool())在多线程环境下仅执行一次。
  • 静态成员 instance_: 使用 std::unique_ptr 管理单例对象的生命周期,避免内存泄漏。

2. 线程池核心逻辑

  • 任务队列: 使用 std::queue<std::function> 存储任务,通过互斥锁 std::mutex 保证线程安全。
  • 工作线程: 在构造函数中创建一组工作线程,每个线程循环从任务队列中取出任务执行。
  • 停止机制: 通过 stop_ 标志和条件变量 std::condition_variable 控制线程池的优雅停止。

3. 线程安全性

  • 初始化安全std::call_once 保证单例对象的初始化在多线程环境下是原子的。
  • 任务提交安全enqueue 方法通过互斥锁保护任务队列的访问。
  • 条件变量同步: 工作线程通过条件变量等待新任务或停止信号。

注意事项

  1. 析构问题: 单例对象的析构依赖于程序结束时的静态成员析构。如果需要更精细的生命周期控制,可以手动调用 delete(需谨慎设计)。
  2. 任务异常: 示例中未处理任务中的异常,实际应用中需添加异常处理逻辑。
  3. 性能优化: 可根据需求调整线程池大小(示例中默认使用硬件并发线程数)。

对比其他实现

  • C++11 局部静态变量单例
static ThreadPool& getInstance() {
 static ThreadPool instance;
 return instance;
}
  • C++11 保证局部静态变量的初始化是线程安全的,但 std::call_once 更适合需要复杂初始化逻辑的场景。
  • 双重检查锁定(DCLP): 传统双重检查锁定在 C++11 前存在风险,std::call_once 是更安全的替代方案。

这种实现结合了单例模式的线程安全性和线程池的任务调度能力,适用于需要全局唯一线程池的场景(如网络服务器、并行计算框架等)。

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4