广西 南宁 微信微网站开发,汽车网站页面,wordpress的安装包,珠海网络营销网站建设1. 单例模式单例模式是一种常用的设计模式#xff0c;它确保一个类只有一个实例#xff0c;并提供一个全局访问点来获取这个实例。这种模式在需要控制资源访问、管理共享状态或协调系统行为时非常有用。单例模式的核心特点#xff1a;私有构造函数#xff1a;防止外部通过n…1. 单例模式单例模式是一种常用的设计模式它确保一个类只有一个实例并提供一个全局访问点来获取这个实例。这种模式在需要控制资源访问、管理共享状态或协调系统行为时非常有用。单例模式的核心特点私有构造函数防止外部通过new关键字创建实例静态成员函数提供全局访问点静态成员函数getInstance()禁止拷贝和赋值通过delete关键字禁用拷贝构造函数和赋值运算符。单例模式的应用场景日志系统确保所有日志都写入同一个日志文件配置管理全局共享一份配置信息数据库连接池统一管理数据库连接设备管理器如打印机等硬件设备的管理。需要注意的是单例模式虽然方便但也会带来一些问题如增加代码耦合度、不利于单元测试等因此在使用时需要权衡利弊。在 C 中单例模式主要有两种实现方式饿汉式和懒汉式它们的核心区别在于实例创建的时机不同。1.1 饿汉式实现饿汉式在程序启动时类加载时就创建唯一实例不管后续是否会使用到。
class EagerSingleton {
private:// 私有构造函数防止外部创建实例EagerSingleton() {}// 禁用拷贝构造和赋值运算EagerSingleton(const EagerSingleton) delete;EagerSingleton operator(const EagerSingleton) delete;// 静态成员变量程序启动时就初始化static EagerSingleton instance;public:// 提供全局访问点static EagerSingleton getInstance() {return instance;}
};// 在类外初始化静态成员
EagerSingleton EagerSingleton::instance;饿汉式特点线程安全由于实例在程序启动时就创建不存在多线程竞争问题可能浪费资源如果单例从未被使用也会占用内存实现简单不需要考虑线程同步问题。1.2 懒汉式实现懒汉式在第一次调用getInstance()方法时才创建实例实现了 延迟初始化。
class LazySingleton {
private:// 私有构造函数LazySingleton() {}// 禁用拷贝构造和赋值运算LazySingleton(const LazySingleton) delete;LazySingleton operator(const LazySingleton) delete;public:// 提供全局访问点C11后局部静态变量初始化是线程安全的static LazySingleton getInstance() {// 第一次调用时才初始化实例static LazySingleton instance;return instance;}
};或者
class LazySingleton {
private:// 私有构造函数LazySingleton() {}// 私有析构函数~LazySingleton() {instance nullptr;}// 禁用拷贝构造和赋值运算LazySingleton(const LazySingleton) delete;LazySingleton operator(const LazySingleton) delete;// 使用指针的方式来访问唯一实例static LazySingleton *instance;public:// 此方式无法保证线程安全应当加锁保护static LazySingleton* const getInstance() {// 第一次调用时才初始化实例if(instance nullptr)instance new LazySingleton();return instance;}
};LazySingleton* LazySingleton::instance nullptr;前者和饿汉式一样实例在创建之后就不会消失后者的实例则可以在被销毁之后重新申请。懒汉式特点延迟初始化节省资源只有在真正需要时才创建实例线程安全C11 及以上标准保证局部静态变量的初始化是线程安全的可能影响首次访问性能第一次调用时需要完成初始化。1.3 总结饿汉式懒汉式实例创建时机程序启动时首次使用时线程安全性天然安全C11 后安全资源利用可能浪费更高效实现复杂度简单稍复杂需考虑线程安全实际开发中懒汉式因为其资源利用效率更高而更常用尤其是在单例可能不会被使用的场景下。而饿汉式适合在程序启动时就需要初始化的核心组件。2. 用单例模式设计线程池2.1 什么是线程池线程池是一种线程管理机制它预先创建一定数量的线程通过复用这些线程来处理多个任务从而避免频繁创建和销毁线程带来的性能开销提高系统效率和资源利用率。线程池包含一个线程队列和一个任务队列线程队列存储预先创建的空闲线程等待处理任务。任务队列存放待执行的任务当线程空闲时会从队列中获取任务执行。2.2 如何将任务传递给线程首先任务队列一定是定义在线程池内部的我们要使线程访问到任务队列及保护任务队列的锁就要使这些线程能访问到线程池本身。所以我们在线程池内部定义线程的运行函数即不断循环访问任务队列获取任务
void ThreadHandler()
{while (true){Task task;{LockGuard lockguard(_queue_mutex);while (_queue.empty() _isrunning){_not_empty.wait(_queue_mutex);}if (_queue.empty() !_isrunning)break;task _queue.front();_queue.pop();}task();}LOG(LogLevel::DEBUG) 线程[ Thread::GetMyName() ]退出...;
}但是直接将该函数传递给线程是不行的。例如在构造线程时直接将该函数以及this传过去
ThreadPool(unsigned int thread_num default_thread_num): _isrunning(true)
{if (thread_num 0)throw ThreadPoolException(线程数量过少!);for (int i 1; i thread_num; i)_threads.emplace_back(thread- std::to_string(i), ThreadHandler, this);
}编译就会出现如下报错这个编译错误的核心原因是非静态成员函数ThreadHandler不能直接作为函数名传递给线程构造函数。非静态成员函数的调用必须依赖于一个类的实例this指针而直接进行传参时编译器无法自动关联实例因此判定为 “无效使用非静态成员函数”。让线程执行非静态成员函数的正确方式是使用lambda表达式捕捉this并包装该函数
[this] { ThreadHandler();
}然后像这样传参
_threads.emplace_back(thread- std::to_string(i), [this]{ ThreadHandler(); });2.3 懒汉实现1
#pragma once
#include vector
#include queue
#include Mutex.hpp
#include Thread.hpp
#include Cond.hpp
#include Log.hppusing namespace MutexModule;
using namespace ThreadModule;
using namespace CondModule;
using namespace LogModule;namespace ThreadPoolModule
{class ThreadPoolException : public std::runtime_error{public:explicit ThreadPoolException(const std::string message): std::runtime_error(ThreadPoolException: message) {}};static const unsigned int default_thread_num 6;static const unsigned int default_capacity 9;template typename Taskclass ThreadPool{private:ThreadPool(unsigned int thread_num default_thread_num): _isrunning(true){if (thread_num 0)throw ThreadPoolException(线程数量过少!);for (int i 1; i thread_num; i)_threads.emplace_back(thread- std::to_string(i), [this]{ ThreadHandler(); });}~ThreadPool(){}ThreadPool(const ThreadPoolTask) delete;ThreadPoolTask operator(const ThreadPoolTask) delete;void ThreadHandler(){while (true){Task task;{LockGuard lockguard(_queue_mutex);while(_queue.empty() _isrunning){_not_empty.wait(_queue_mutex);}if(_queue.empty() !_isrunning)break;task _queue.front();_queue.pop();}task();}LOG(LogLevel::DEBUG) 线程[ Thread::GetMyName() ]退出...;}void Start(){for (auto thread : _threads)thread.Start();LOG(LogLevel::DEBUG) 线程池已启动...;}public:void Stop(){_isrunning false;_not_empty.broadcast();LOG(LogLevel::DEBUG) 线程池开始停止...;}void Wait(){if(_isrunning)throw ThreadPoolException(等待前线程池未停止!);for (auto thread : _threads)thread.Join();LOG(LogLevel::DEBUG) 等待成功, 线程已全部退出...;}void PushTask(const Task task){if(!_isrunning)throw ThreadPoolException(线程池已停止, 无法继续增加任务!);_queue.push(task);_not_empty.signal();}static ThreadPoolTask GetInstance(){static ThreadPoolTask Instance;Instance.Start();return Instance;}private:bool _isrunning;std::vectorThread _threads;std::queueTask _queue;Mutex _queue_mutex;Cond _not_empty;};
}2.4 懒汉实现2
#pragma once
#include vector
#include queue
#include Mutex.hpp
#include Thread.hpp
#include Cond.hpp
#include Log.hppusing namespace MutexModule;
using namespace ThreadModule;
using namespace CondModule;
using namespace LogModule;namespace ThreadPoolModule
{class ThreadPoolException : public std::runtime_error{public:explicit ThreadPoolException(const std::string message): std::runtime_error(ThreadPoolException: message) {}};static const unsigned int default_thread_num 6;static const unsigned int default_capacity 9;template typename Taskclass ThreadPool{private:ThreadPool(unsigned int thread_num default_thread_num): _isrunning(true){if (thread_num 0)throw ThreadPoolException(线程数量过少!);for (int i 1; i thread_num; i)_threads.emplace_back(thread- std::to_string(i), [this]{ ThreadHandler(); });}~ThreadPool(){_ins nullptr;}ThreadPool(const ThreadPoolTask) delete;ThreadPoolTask operator(const ThreadPoolTask) delete;void ThreadHandler(){while (true){Task task;{LockGuard lockguard(_queue_mutex);while(_queue.empty() _isrunning){_not_empty.wait(_queue_mutex);}if(_queue.empty() !_isrunning)break;task _queue.front();_queue.pop();}task();}LOG(LogLevel::DEBUG) 线程[ Thread::GetMyName() ]退出...;}void Start(){for (auto thread : _threads)thread.Start();LOG(LogLevel::DEBUG) 线程池已启动...;}public:void Stop(){_isrunning false;_not_empty.broadcast();LOG(LogLevel::DEBUG) 线程池开始停止...;}void Wait(){if(_isrunning)throw ThreadPoolException(等待前线程池未停止!);for (auto thread : _threads)thread.Join();delete _ins;LOG(LogLevel::DEBUG) 等待成功, 线程已全部退出...;}void PushTask(const Task task){if(!_isrunning)throw ThreadPoolException(线程池已停止, 无法继续增加任务!);_queue.push(task);_not_empty.signal();}static ThreadPoolTask *const GetInstance(){if (_ins nullptr){LockGuard lockguard(_mutex);if (_ins nullptr){try{_ins new ThreadPoolTask();}catch (const std::exception e){std::cerr e.what() \n;}_ins-Start();}}return _ins;}private:bool _isrunning;std::vectorThread _threads;std::queueTask _queue;static ThreadPoolTask *_ins;Mutex _queue_mutex;static Mutex _mutex;Cond _not_empty;};template typename TaskThreadPoolTask *ThreadPoolTask::_ins nullptr;template typename TaskMutex ThreadPoolTask::_mutex;
}相比较于第一种第二种可以在调用Stop以及Wait函数之后重新启动。