【Linux】单例模式与线程池(含实战代码)
单例模式
概念介绍
在面向对象编程中,我们通常借助构造函数快速创建对象实例,这是对象实例化的常规方式。但在实际开发中,若某个对象包含海量数据(例如占用几十甚至上百 GB 的内存空间),如果频繁通过构造函数创建该对象的多个实例,会导致内存占用急剧膨胀,不仅会让程序体积异常庞大,还可能引发内存溢出、性能大幅下降等严重问题。
单例模式(Singleton Pattern)正是为解决这类问题而生的经典设计模式:它通过将类的构造函数私有化,同时显式禁用拷贝构造函数和赋值运算符(C++ 中),从语法层面杜绝外部随意创建类的实例;并通过类内部提供的全局访问接口,确保整个程序的生命周期内,该类仅存在唯一的一个实例对象。这既避免了重复创建大体积对象带来的内存浪费,也保证了全局范围内对该唯一实例的统一访问,有效提升程序的资源利用率和运行稳定性。
两种使用方式
饿汉方式
假设有一个壮汉,饿汉式的创建方式就好比这个壮汉刚吃完饭,不等后续使用,就立刻把碗洗干净并放好 —— 对应到单例模式的实现逻辑中,就是程序一启动(进程初始化阶段),不管后续是否会用到这个单例对象,都会立即完成实例的初始化和创建。
这种 “提前创建” 的特性,虽然能保证实例的线程安全性(无需额外加锁)、获取实例时的效率极高,但也存在明显的弊端:如果这个单例对象占用的内存资源较多(比如包含大体积数据),且程序运行全程都没有实际使用该实例,就会造成内存资源的无意义浪费。因此在实际开发中,饿汉式单例的使用场景相对有限,更多用于实例体积小、必定会被使用的场景。
demo演示
#include <iostream>
#include <string>
using namespace std;
class Singleton
{
private:
Singleton()
{
}
// 显式禁用拷贝构造接口
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
static Singleton instance;
public:
// 实例获取接口
static Singleton &getInstance()
{
return instance;
}
};
// 关键步骤,类外定义程序加载直接进行初始化
Singleton Singleton::instance;
// 主函数测试
int main()
{
Singleton &obj1 = Singleton::getInstance();
}
懒汉方式
懒汉式单例模式与饿汉式的核心差异,恰好体现在你比喻的 “洗碗时间” 上:如果说饿汉式是 “吃完饭立马洗碗”,那懒汉式就是 “吃完饭不着急洗碗,非要等到下一顿要用餐、需要用到干净碗的时候,才去洗碗”。
对应到技术实现上,懒汉式单例不会在程序启动 / 加载阶段提前创建实例,而是将实例的初始化操作完全 “延迟”—— 只有当程序首次调用获取该单例的接口、真正需要使用这个实例时,才会触发实例的创建逻辑。这种 “按需创建” 的特性,能从根本上避免饿汉式可能出现的 “未使用却占用内存” 的资源浪费问题,也是懒汉式在实际开发中更常用的核心原因。
demo演示
#include <iostream>
#include <string>
#include <mutex>
using namespace std;
class Singleton
{
private:
Singleton() {}
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
static Singleton *instance;
static mutex mtx; // 互斥锁:保证多线程下实例创建的线程安全
public:
// 外部获取实例接口
static Singleton &getInstance()
{
// 双重加锁防止多线程首次判断多次新建实例
if (instance == nullptr)
{
lock_guard<mutex> lock(mtx);
if (instance == nullptr)
{
instance = new Singleton();
}
}
return *instance;
}
};
Singleton *Singleton::instance = nullptr;
mutex Singleton::mtx;
// 主函数测试
int main()
{
Singleton &obj1 = Singleton::getInstance();
}
线程池
池化技术
池化技术是一种资源管理优化策略,核心思想可以用一个通俗的比喻理解:
就像餐厅不会在每次有客人点餐时才临时招聘厨师、购买厨具(高成本、慢响应),而是提前招聘好固定数量的厨师、准备好足够的厨具放在 “资源池” 里,客人点餐时直接从池子里调配厨师 / 厨具,用完后放回池子而非销毁 —— 池化技术就是对编程中 “昂贵资源” 的这种 “预创建、复用、统一管理” 的思路。
1. 核心定义(专业表述)
在编程中,针对创建 / 销毁成本高、使用频繁的资源(如线程、数据库连接、内存、网络连接等),提前创建一批资源并放入 “资源池” 中;当程序需要使用资源时,直接从池中获取,使用完毕后不销毁资源,而是归还到池中供后续复用;同时通过统一管理资源池的大小、分配规则,避免资源频繁创建 / 销毁的性能损耗,提升程序整体效率。
线程池创建思路
线程池的创建思路很简单——线程池启动的时候提前创建若干个线程,利用条件变量与循环不断进行任务等待与执行,其实本质上也就是一个生产者-消费者模型,生产者是外部进行线程池调用的线程,消费者就是线程池内的线程对象,_task_queue就是共享缓存区。

这里我们结合单例模式的懒汉方式进行线程池创建,利用之前的日志程序完成有序的消息打印。
代码实战——单例模式结合线程池的使用
线程的封装
#ifndef _THREAD
#define _THREAD
#include <pthread.h>
#include <functional>
#include <string>
#include <iostream>
#include "Log.hpp"
namespace Modelthread
{
using namespace LogModule;
using func_t = std::function<void()>;
static int count = 1;
class Thread
{
private:
void Enabledetach()
{
_isdetach = true;
}
void Enablerunning()
{
_isrunning = true;
}
std::string getname()
{
char name[128];
snprintf(name, sizeof(name), "thread-%d", count++);
return name;
}
public:
Thread(func_t func) : _pid(0), _isdetach(false), _isrunning(false), _func(func)
{
_name = getname();
}
static void *routinue(void *x)
{
Thread *p = static_cast<Thread *>(x);
if (p->_isdetach)
{
p->Detach();
}
pthread_t Pid = p->_pid;
pthread_setname_np(Pid, p->_name.c_str());
p->_func();
return nullptr;
}
void Detach()
{
pthread_detach(_pid);
Enabledetach();
}
void Start()
{
pthread_create(&_pid, nullptr, routinue, this);
Enablerunning();
}
void Stop()
{
if (pthread_cancel(_pid) != 0)
{
std::cerr << "cancel: " << std::endl;
}
}
bool Join()
{
if (_isdetach)
{
LOG(LEVEL::INFO) << "thread has been detached!\n";
return false;
}
if (pthread_join(_pid, nullptr) != 0)
{
std::cerr << "join: " << std::endl;
return false;
}
else
{
LOG(LEVEL::INFO) << _name << " join success\n";
return true;
}
return false;
}
~Thread()
{
}
private:
pthread_t _pid;
func_t _func;
std::string _name;
bool _isdetach;
bool _isrunning;
};
};
#endif
锁的封装
// Mutex.hpp
#pragma once
#include <iostream>
#include <pthread.h>
namespace MutexModule
{
class Mutex
{
public:
Mutex()
{
pthread_mutex_init(&_mutex, nullptr);
}
pthread_mutex_t *Get()
{
return &_mutex;
}
void Lock()
{
int n = pthread_mutex_lock(&_mutex);
(void)n;
}
void Unlock()
{
int n = pthread_mutex_unlock(&_mutex);
(void)n;
}
~Mutex()
{
pthread_mutex_destroy(&_mutex);
}
private:
pthread_mutex_t _mutex;
};
Mutex defaultmutex;
class LockGuard
{
public:
LockGuard(Mutex &mutex = defaultmutex) : _mutex(mutex)
{
_mutex.Lock();
}
~LockGuard()
{
_mutex.Unlock();
}
private:
Mutex &_mutex;
};
}
条件变量的封装
#pragma once
#include <iostream>
#include <pthread.h>
#include "Mutex.hpp"
using namespace MutexModule;
namespace CondModule
{
class Cond
{
public:
Cond()
{
pthread_cond_init(&_cond, nullptr);
}
void Wait(Mutex &mutex)
{
int n = pthread_cond_wait(&_cond, mutex.Get());
(void)n;
}
void Signal()
{
// 唤醒在条件变量下等待的一个线程
int n = pthread_cond_signal(&_cond);
(void)n;
}
void Broadcast()
{
// 唤醒所有在条件变量下等待的线程
int n = pthread_cond_broadcast(&_cond);
(void)n;
}
~Cond()
{
pthread_cond_destroy(&_cond);
}
private:
pthread_cond_t _cond;
};
};
生产者线程模拟
#include <functional>
#include <iostream>
#include <vector>
#include <time.h>
#include "Log.hpp"
using namespace LogModule;
void Download()
{
LOG(LEVEL::INFO) << "我是一个下载任务\n";
}
void Upload()
{
LOG(LEVEL::INFO) << "我是一个上传任务\n";
}
void Updata()
{
LOG(LEVEL::INFO) << "我是一个更新任务\n";
}
std::function<void()> func1 = Download;
std::function<void()> func2 = Upload;
std::function<void()> func3 = Updata;
class Task
{
private:
void init_srand()
{
srand((unsigned int)time(0)); ;
}
public:
Task()
{
tasks.push_back(func1);
tasks.push_back(func2);
tasks.push_back(func3);
init_srand();
}
// 返回随机任务
std::function<void()> dispatch()
{
int _random = rand() % 3;
return tasks[_random];
}
~Task()
{
}
private:
std::vector<std::function<void()>> tasks;
};
日志的封装
#pragma once
#include <string>
#include <iostream>
#include "Mutex.hpp"
#include <cstdio>
#include <sstream>
#include <fstream>
#include <unistd.h>
#include <sys/types.h>
#include <memory>
#include <filesystem>
using namespace MutexModule;
namespace LogModule
{
#define gsep "\r\n"
class LogStrategy
{
public:
~LogStrategy() = default;
virtual void SyncLog(const std::string &info) = 0;
};
// 显示器打印
class ScreenStratey : public LogStrategy
{
public:
void SyncLog(const std::string &info) override
{
LockGuard _lock(_mutex);
std::cout << info << gsep;
}
private:
Mutex _mutex;
};
// 指定文件打印
std::string defaultpath = "./Log";
std::string defaultfile = "Mylog.log";
class FileStrategy : public LogStrategy
{
public:
FileStrategy(const std::string path = defaultpath, const std::string filename = defaultfile)
: _path(path),
_filename(filename)
{
if (std::filesystem::exists(_path))
{
return;
}
try
{
std::filesystem::create_directories(_path);
}
catch (const std::filesystem::filesystem_error &e)
{
std::cerr << e.what() << '\n';
}
}
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::string filename = _path + (_path.back() == '/' ? "" : "/") + _filename; // "./log/" + "my.log"
std::ofstream out(filename, std::ios::app); // 追加写入的 方式打开
if (!out.is_open())
{
return;
}
out << message << gsep;
out.close();
}
~FileStrategy() {};
private:
std::string _path;
std::string _filename;
Mutex _mutex;
};
enum class LEVEL
{
DEBUG,
INFO,
WARNING,
ERROR,
FATAL
};
std::string leveltos(const LEVEL &level)
{
switch (level)
{
case LEVEL::DEBUG:
return "DEBUG";
case LEVEL::INFO:
return "INFO";
case LEVEL::WARNING:
return "WARNING";
case LEVEL::ERROR:
return "ERROR";
case LEVEL::FATAL:
return "FATAL";
default:
return "UNKNOW";
}
}
std::string GetTime()
{
time_t curr = time(nullptr);
struct tm curr_tm;
localtime_r(&curr, &curr_tm);
char timebuffer[128];
snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
curr_tm.tm_year + 1900,
curr_tm.tm_mon + 1,
curr_tm.tm_mday,
curr_tm.tm_hour,
curr_tm.tm_min,
curr_tm.tm_sec);
return timebuffer;
}
// 默认执行类
class Logger
{
public:
Logger()
{
Enable_Screen_log_Strategy();
}
void Enable_Screen_log_Strategy()
{
ffulsh_strategy = std::make_unique<ScreenStratey>();
}
void Enable_File_log_Strategy()
{
ffulsh_strategy = std::make_unique<FileStrategy>();
}
class Logmessage
{
public:
Logmessage(const LEVEL &level, std::string filename, int line, Logger &logger)
: _ctime(GetTime()),
_level(level),
_pid(getpid()),
_filename(filename),
_line(line),
_logger(logger)
{
std::stringstream ss;
ss << "[" << _ctime << "] "
<< "[" << leveltos(_level) << "] "
<< "[" << _pid << "] "
<< "[" << _filename << "] "
<< "[" << _line << "] "
<< "- ";
finfo = ss.str();
}
template <typename T>
Logmessage &operator<<(const T &info)
{
std::stringstream ss;
ss << info;
finfo += ss.str();
return *this;
}
~Logmessage()
{
_logger.ffulsh_strategy->SyncLog(finfo);
}
private:
std::string _ctime;
LEVEL _level;
pid_t _pid;
std::string _filename;
int _line;
Logger &_logger;
std::string finfo; // 完整信息
};
Logmessage operator()(LEVEL level, std::string name, int line)
{
return Logmessage(level, name, line, *this);
}
~Logger()
{
}
private:
std::unique_ptr<LogStrategy> ffulsh_strategy;
};
Logger logger;
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Screen_log_Strategy logger.Enable_Screen_log_Strategy()
#define Enable_File_log_Strategy logger.Enable_File_log_Strategy()
}
// 日志格式:[ctime][level][pid][filename][line]
线程池
#pragma once
#include "Thread.hpp"
#include <vector>
#include "Task.hpp"
#include <queue>
#include <unistd.h>
#include "Log.hpp"
#include "Cond.hpp"
#include "Mutex.hpp"
namespace ThreadPool
{
using namespace Modelthread;
using namespace CondModule;
using namespace MutexModule;
using namespace LogModule;
class threadpool
{
private:
void Wakeup_one()
{
_cond.Signal();
}
void Wakeup_all()
{
_cond.Broadcast();
}
threadpool()
: _isrunning(false),
_sleeping(0),
_tnum(5)
{
for (int i = 1; i <= _tnum; i++)
{
_threads.emplace_back(
[this]()
{
Handler();
});
}
}
void Start()
{
if (_isrunning)
return;
_isrunning = true;
for (auto &_thread : _threads)
{
_thread.Start();
LOG(LEVEL::INFO) << "new thread created!\n";
}
}
threadpool(const threadpool &) = delete;
threadpool &operator=(const threadpool &) = delete;
public:
static threadpool *Getinstance()
{
if (inc == nullptr)
{
LockGuard _glg(_glock);
if (inc == nullptr)
{
inc = new threadpool;
inc->Start();
LOG(LEVEL::INFO) << "首次创建线程池,线程池启动...\n";
}
}
else
{
LOG(LEVEL::INFO) << "线程池已创建,返回...\n";
}
return inc;
}
void Stop()
{
if (_isrunning == false)
{
return;
}
_isrunning = false;
LOG(LEVEL::INFO) << "线程池即将退出...\n"; ;
Wakeup_all();
}
void Join()
{
for (auto &_thread : _threads)
{
_thread.Join();
}
}
bool Enqueue()
{
if (_isrunning)
{
static Task t;
{
LockGuard _lg(_mutex);
_task_queue.push(t.dispatch());
if (_sleeping == _threads.size())
Wakeup_one(); ;
}
return true;
}
return 0;
}
void Handler()
{
func_t _task;
while (true)
{
{
char name[128];
pthread_getname_np(pthread_self(), name, sizeof(name));
LockGuard _lock(_mutex);
while (_task_queue.empty() && _isrunning)
{
_sleeping++;
_cond.Wait(_mutex); ;
_sleeping--;
}
if (_task_queue.empty() && !_isrunning)
{
LOG(LEVEL::INFO) << "任务队列为空" << name << "即将退出...\n";
break;
}
_task = _task_queue.front();
_task_queue.pop();
}
_task();
}
}
private:
std::vector<Thread> _threads;
int _tnum;
bool _isrunning;
Cond _cond;
std::queue<func_t> _task_queue;
Mutex _mutex;
int _sleeping;
static threadpool *inc;
static Mutex _glock;
};
threadpool *threadpool::inc = nullptr;
Mutex threadpool::_glock;
};
主函数调用示例
#include "Log.hpp"
#include "ThreadPool.hpp"
#include <unistd.h>
using namespace ThreadPool;
int main()
{
threadpool *tp = threadpool::Getinstance();
int cnt = 10;
while (cnt--)
{
tp->Enqueue();
}
sleep(1);
tp->Stop();
tp->Join();
}
更多推荐



所有评论(0)