目录
【1】什么是单例模式
【2】什么是设计模式
【3】单例模式的特点
【4】饿汉实现方式和懒汉实现方式
【5】饿汉方式实现单例模式
【6】懒汉方式实现单例模式
【7】将线程池改为单例模式
单例模式是一种 "经典的, 常用的, 常考的" 设计模式。
IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖大佬的后腿, 于是大佬们针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是 设计模式。
某些类, 只应该具有一个对象(实例), 就称之为单例,例如一个男人只能有一个媳妇。
在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百G) 到内存中. 此时往往要用一个单例的类来管理这些数据。
【洗完的例子】
吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭。
吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式。
懒汉方式最核心的思想是 "延时加载". 从而能够优化服务器的启动速度。
template
class Singleton {
static T data;
public:
static T* GetInstance() {
return &data;
}
};
template
class Singleton {
static T* inst;
public:
static T* GetInstance() {
if (inst == NULL) {
inst = new T();
}
return inst;
}
};
【Makefile文件】
# 创建变量关联关系
cc=c++
standard=-std=c++11
linkLib=-l pthread
# 创建编译文件依赖关系
myThreadPool:ThreadPool.cc
$(cc) -o $@ $^ $(standard) $(linkLib)
# 创建删除命令
.PHONY:clean
clean:
rm -f myThreadPool
【LockGuard.hpp文件】
#pragma once
#include
/* 锁控制类 */
class Mutex {
public:
/* 构造函数 */
Mutex(pthread_mutex_t* lock = nullptr)
: _tdLock(lock)
{}
/* 析构函数 */
~Mutex(){};
/* 加锁函数 */
void Lock() {
if(_tdLock != nullptr) { pthread_mutex_lock(_tdLock); }
}
/* 解锁函数 */
void unLock() {
if(_tdLock != nullptr) { pthread_mutex_unlock(_tdLock);}
}
private:
pthread_mutex_t* _tdLock;
};
/* 锁操作类 */
class LockGuard {
public:
/* 构造函数 */
LockGuard(pthread_mutex_t* mutex)
: _mutex(mutex)
{
_mutex.Lock();
}
/* 析构函数 */
~LockGuard() {
_mutex.unLock();
}
private:
Mutex _mutex;
};
【Thread.hpp文件】
#pragma once
#include
#include
#include
#include
namespace ThreadNs
{
/* 线程连接上下文 */
class Thread;
class ConnectText
{
public:
/* 构造函数 */
ConnectText() : _this(nullptr), _args(nullptr) {}
/* 析构函数 */
~ConnectText() {}
public:
Thread *_this;
void *_args;
};
/* 线程类封装 */
class Thread
{
public:
using func_t = std::function; // // 从定义类似函数指针类型:返回值是:void* 参数是:void*
public:
/* 构造函数 */
Thread(const int number = 0)
{
// 创建线程的名称
char buffer[64];
snprintf(buffer, sizeof(buffer), "Thread-%d", number);
_tName = buffer;
}
/* 析构函数 */
~Thread() {}
public:
/* 线程启动 */
void Start(const func_t& func, void *args = nullptr)
{
// 建立关系
_func = func;
_args = args;
// 创建线程后,启动
ConnectText *cnt = new ConnectText();
cnt->_this = this;
cnt->_args = _args;
int n = pthread_create(&_tid, nullptr, StartRoutine, (void *)cnt);
assert(n == 0); (void)n;
// 编译debug的方式时assert是存在的,release方式assert是不存在的,到时n就是定义了,但是没有被使用的变量。
// 在有的编译器下会有warning。
}
/* 线程等待 */
void Join()
{
int n = pthread_join(_tid, nullptr);
assert(n == 0);
(void)n;
}
public:
/* 获取线程名字 */
std::string GetThreadName()
{
return _tName;
}
private:
/* 线程函数 */
static void *StartRoutine(void *args)
{ // 在类内创建线程,想让线程执行对应的方法,需要将方法设置称为static
ConnectText *cnt = static_cast(args);
void *exRet = cnt->_this->RoutineRun(cnt->_args);
delete cnt;
return exRet;
}
void *RoutineRun(void *args)
{
return _func(args);
}
private:
pthread_t _tid; // 线程id
std::string _tName; // 线程名称
func_t _func; // 线程函数
void *_args; // 线程参数
};
}
【Task.hpp文件】
#pragma once
#include
#include
#include
/* 计算任务 */
class CalTask
{
public:
using func_t = std::function;
public:
/* 构造函数 */
CalTask() {}
/* 构造函数 */
CalTask(int x, int y, char op, func_t func)
: _x(x), _y(y), _op(op), _callBalk(func)
{
}
public:
/* 仿函数 */
std::string operator()()
{
int result = _callBalk(_x, _y, _op);
char buffer[64];
snprintf(buffer, sizeof(buffer), "%d %c %d = %d", _x, _op, _y, result);
return buffer;
}
public:
/* 返回打印公式 */
std::string ToTaskString()
{
char buffer[64];
snprintf(buffer, sizeof(buffer), "%d %c %d = ?", _x, _op, _y);
return buffer;
}
private:
int _x;
int _y;
char _op;
func_t _callBalk;
};
/* 执行计算的方法 */
int MyCalculate(int x, int y, char op)
{
int result = 0;
switch (op)
{
case '+':
result = x + y;
break;
case '-':
result = x - y;
break;
case '*':
result = x * y;
break;
case '/':
{
if (y == 0)
{
std::cerr << "div zero error!" << std::endl;
result = -1;
}
else
{
result = x / y;
}
break;
}
case '%':
{
if (y == 0)
{
std::cerr << "mod zero error!" << std::endl;
result = -1;
}
else
{
result = x % y;
}
break;
}
default:
break;
}
return result;
}
/* 保存任务 */
class SaveTask
{
public:
using func_t = std::function;
public:
/* 构造函数 */
SaveTask() {}
/* 构造函数 */
SaveTask(const std::string &message, func_t func)
: _message(message), _callBalk(func)
{
}
public:
/* 仿函数 */
void operator()()
{
_callBalk(_message);
}
private:
std::string _message;
func_t _callBalk;
};
/* 保存方法 */
void Save(const std::string& massage){
std::string target = "./log.txt";
FILE *fp = fopen(target.c_str(), "a+");
if(fp == NULL){
std::cerr << "fopen fail!" << std::endl;
return;
}
fputs(massage.c_str(), fp);
fputs("\n", fp);
fclose(fp);
}
【ThreadPool.hpp文件】
#pragma once
#include
#include
#include
#include
#include
#include "Thread.hpp"
#include "LockGuard.hpp"
/* 引用命名空间 */
using namespace ThreadNs;
/* 线程上下文数据 */
template
class ThreadPool;
template
class ThreadData
{
public:
ThreadData(ThreadPool *tp, const std::string &n)
: _threadPool(tp), _name(n)
{}
public:
ThreadPool *_threadPool;
std::string _name;
};
/* 线程池封装 */
const int g_num = 5; // 设置线程数
template
class ThreadPool
{
public:
/* (单例模式获取对象方法) */
static ThreadPool *GetInstance()
{
if(nullptr == tp)
{
_singlock.lock();
if(nullptr == tp)
{
tp = new ThreadPool();
}
_singlock.unlock();
return tp;
}
}
/* 析构函数 */
~ThreadPool()
{
// 释放线程锁
pthread_mutex_destroy(&_mutex);
// 释放线程条件变量
pthread_cond_destroy(&_cond);
// 遍历释放
for (auto &t : _threads)
{
delete t;
}
}
public:
/* 开启线程池 */
void Run()
{
// 启动线程
for (const auto &t : _threads)
{
ThreadData *td = new ThreadData(this, t->GetThreadName());
t->Start(HandlerTask, td);
std::cout << t->GetThreadName() << ": Start..." << std::endl;
}
// // 阻塞式等待回收线程
// for (const auto &t : _threads)
// {
// t->Join();
// std::cout << t->GetThreadName() << ": Recycle..." << std::endl;
// }
}
public:
/* 获取锁 */
pthread_mutex_t *Mutex() { return &_mutex; }
/* 线程加锁 */
void Lock() { pthread_mutex_lock(&_mutex); }
/* 线程解锁 */
void UnLock() { pthread_mutex_unlock(&_mutex); }
/* 线程等待 */
void ThreadWait() { pthread_cond_wait(&_cond, &_mutex); }
/* 判断是否有任务 */
bool IsEmpty() { return _taskQueue.empty(); }
/* 新增任务 */
void Push(const T &in)
{
// 加锁->自动解锁
LockGuard LockGuard(&_mutex);
// 新增任务
_taskQueue.push(in);
// 环形线程执行
pthread_cond_signal(&_cond);
}
/* 执行任务 */
T Pop()
{
// 创建T对象
T t;
// 获取栈顶任务
t = _taskQueue.front();
_taskQueue.pop();
// 返回任务
return t;
}
private:
/* 构造函数(改写为单例模式) */
ThreadPool(const int &num = g_num)
: _threadNum(num)
{
// 初始化线程锁
pthread_mutex_init(&_mutex, nullptr);
// 初始化线程条件变量
pthread_cond_init(&_cond, nullptr);
// 将每个线程地址Push到线程地址容器中
for (int i = 0; i < _threadNum; i++)
{
_threads.push_back(new Thread(i + 1));
}
}
/* 拷贝构造禁用 */
ThreadPool(const ThreadPool&) = delete;
/* 赋值拷贝禁用 */
void operator=(const ThreadPool&) = delete;
/* 线程池共享线程 */
static void *HandlerTask(void* args)
{
ThreadData *td = static_cast *>(args);
while (true)
{
// 创建任务对象
T t;
{
// 加锁->自动解锁
LockGuard LockGuard(td->_threadPool->Mutex());
// 如果没有任务,等待任务
if (td->_threadPool->IsEmpty())
{
td->_threadPool->ThreadWait();
}
t = td->_threadPool->Pop();
// 解锁
td->_threadPool->UnLock();
}
// 打印信息
std::cout << td->_name << ":承接一个任务[" << t.ToTaskString() << "],任务的执行结果是[" << t() << "]" << std::endl;
}
return nullptr;
}
private:
int _threadNum; // 线程数量
std::vector _threads; // 存放线程地址的容器
std::queue _taskQueue; // 存放线程任务的队列
pthread_mutex_t _mutex; // 线程锁
pthread_cond_t _cond; // 线程条件变量
static ThreadPool *tp;
static std::mutex _singlock;
};
template
ThreadPool * ThreadPool::tp = nullptr;
template
std::mutex ThreadPool::_singlock;
【ThreadPool.cc文件】
#include
#include
#include
#include "Task.hpp"
#include "ThreadPool.hpp"
using namespace std;
int main()
{
// 智能指针管理
// unique_ptr> tP(new ThreadPool());
ThreadPool::GetInstance()->Run();
// 启动线程池
// tP->Run();
// 手动派发任务
int x;
int y;
char op;
while (1)
{
std::cout << "请输入数据1# ";
std::cin >> x;
std::cout << "请输入数据2# ";
std::cin >> y;
std::cout << "请输入要进行的运算# ";
std::cin >> op;
CalTask t(x, y, op, MyCalculate);
ThreadPool::GetInstance()->Push(t);
sleep(1);
}
return 0;
}