几天不多说,直接来点实用的,其实,在 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 方法通过互斥锁保护任务队列的访问。
- 条件变量同步: 工作线程通过条件变量等待新任务或停止信号。
注意事项
- 析构问题: 单例对象的析构依赖于程序结束时的静态成员析构。如果需要更精细的生命周期控制,可以手动调用 delete(需谨慎设计)。
- 任务异常: 示例中未处理任务中的异常,实际应用中需添加异常处理逻辑。
- 性能优化: 可根据需求调整线程池大小(示例中默认使用硬件并发线程数)。
对比其他实现
- C++11 局部静态变量单例:
static ThreadPool& getInstance() {
static ThreadPool instance;
return instance;
}
- C++11 保证局部静态变量的初始化是线程安全的,但 std::call_once 更适合需要复杂初始化逻辑的场景。
- 双重检查锁定(DCLP): 传统双重检查锁定在 C++11 前存在风险,std::call_once 是更安全的替代方案。
这种实现结合了单例模式的线程安全性和线程池的任务调度能力,适用于需要全局唯一线程池的场景(如网络服务器、并行计算框架等)。