单例模式是一种“经典的、常用的、常考的”设计模式。
IT行业这么火,涌入的人很多。俗话说林子大了啥鸟都有,大佬和菜鸡们两极分化的越来越严重,为了让菜鸡们不太拖大佬的后腿,于是大佬们针对一些经典的常见的场景,给定了一些对应的解决方案,这个就是设计模式(也是经验模式)
某些类,只应该具有一个对象(实例),就称之为单例。
eg:在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百G) 到内存中,此时往往要用一个单例的类来管理这些数据。
吃完饭,立刻洗碗,这种就是饿汉方式。因为下一顿吃的时候可以立刻拿着碗就能吃饭。
吃完饭,先把碗放下,然后下一顿饭用到这个碗了再洗碗,这就是懒汉方式.
// task.hpp
#pragma once
#include
#include
#include
namespace ns_task
{
class Task
{
private:
int x_;
int y_;
char op_; // +-*/%
public:
Task() {}
Task(int x, int y, char op) : x_(x), y_(y), op_(op)
{
}
std::string Show()
{
std::string message = std::to_string(x_);
message += op_;
message += std::to_string(y_);
message +="=?";
return message;
}
int Run()
{
int res = 0;
switch (op_)
{
case '+':
res = x_ + y_;
break;
case '-':
res = x_ - y_;
break;
case '*':
res = x_ * y_;
break;
case '/':
res = x_ / y_;
break;
case '%':
res = x_ % y_;
break;
default:
std::cout << "错误的运算" << std::endl;
break;
}
std::cout << "当前任务正在被[" << pthread_self() << "]处理" << x_ << op_ << y_ << "=" << res << std::endl;
std::cout << "-----------------------------" << std::endl;
return res;
}
int operator()()
{
return Run();
}
~Task() {}
};
}
///
// thread_pool.hpp
#pragma once
#include
#include
#include
#include // sleep()
#include
namespace ns_threadpool
{
const int g_num = 5;
template <class T>
class ThreadPool
{
private:
int num_; // 这个线程池有多少个线程
std::queue<T> task_queue_; // 任务队列——这是一个临界资源
pthread_mutex_t mtx_;
pthread_cond_t cond_;
static ThreadPool<T> *ins;
private:
ThreadPool(int num = g_num)
: num_(num)
{
pthread_mutex_init(&mtx_, nullptr);
pthread_cond_init(&cond_, nullptr);
}
ThreadPool(const ThreadPool<T> &tp) = delete;
ThreadPool<T> &operator=(ThreadPool<T> &tp) = delete;
public:
static ThreadPool<T> *GetInstance()
{
// 当前单例对象还没有被创建
if (ins == nullptr)
{
ins = new ThreadPool<T>();
ins->InitThreadPool();
std::cout<<"首次加载对象"<<std::endl;
}
return ins;
}
void Lock()
{
pthread_mutex_lock(&mtx_);
}
void UnLock()
{
pthread_mutex_unlock(&mtx_);
}
void Wait()
{
pthread_cond_wait(&cond_, &mtx_);
}
void WakeUp()
{
pthread_cond_signal(&cond_);
}
bool IsEmpty()
{
return task_queue_.empty();
}
public:
// 细节:在类中要让线程执行类的成员方法,是不可行的!!!!!!! InitThreadPool回调Routine
// 解决:必须让线程执行静态方法
static void *Routine(void *args)
{
pthread_detach(pthread_self());
ThreadPool<T> *tp = (ThreadPool<T> *)args;
while (true)
{
// 由于是静态方法,所以在函数内部是无法访问类内成员的
// 所以pthread_create中第四个参数要传递this指针
// if (task_queue_.empty())
// {
// wait();
// }
tp->Lock();
// 首先检测线程池中是否有任务
while (tp->IsEmpty()) //不用if判断,防止伪唤醒
{
// 任务队列为空的话,我们需要挂起等待
tp->Wait();
}
// 当前行,队列中一定是有任务的
T t;
tp->PopTask(&t);
tp->UnLock();
// 当前线程处理任务的时候,其他线程也可能在处理任务,所以run方法写在解锁之外
t();
}
}
void InitThreadPool()
{
pthread_t tid;
for (int i = 0; i < num_; i++)
{
pthread_create(&tid, nullptr, Routine, (void *)this);
}
}
void PushTask(const T &in)
{
Lock();
// 向任务队列中塞入任务
task_queue_.push(in);
UnLock();
// 当任务放进去了之后,就要唤醒线程
WakeUp();
}
void PopTask(T *out)
{
*out = task_queue_.front();
task_queue_.pop();
}
~ThreadPool()
{
pthread_mutex_destroy(&mtx_);
pthread_cond_destroy(&cond_);
}
};
// 静态成员需要在类外初始化
template <class T>
ThreadPool<T> *ThreadPool<T>::ins = nullptr;
}
// main.cc
/**
* @file main.cc
* @author your name (you@domain.com)
* @brief 单例模式下的线程池
* @version 0.1
* @date 2022-09-13
*
* @copyright Copyright (c) 2022
*
*/
#include "thread_pool.hpp"
#include "task.hpp"
#include
#include
using namespace ns_threadpool;
using namespace ns_task;
int main()
{
std::cout << "正在运行我的进程的其他代码...." << std::endl;
std::cout << "正在运行我的进程的其他代码...." << std::endl;
std::cout << "正在运行我的进程的其他代码...." << std::endl;
std::cout << "正在运行我的进程的其他代码...." << std::endl;
std::cout << "正在运行我的进程的其他代码...." << std::endl;
std::cout << "正在运行我的进程的其他代码...." << std::endl;
std::cout << "正在运行我的进程的其他代码...." << std::endl;
sleep(5);
srand((unsigned int)time(nullptr));
while (true)
{
sleep(1);
Task t(rand() % 20 + 1, rand() % 10 + 1, "+-*/%"[rand() % 5]);
// 使用单例
ThreadPool<Task>::GetInstance()->PushTask(t);
// 打印地址
std::cout<<"当前对象的地址:"<<ThreadPool<Task>::GetInstance()<<std::endl;
}
return 0;
}
运行结果:
注意:单例本身会在任何场景,任何环境下被调用,GetInstance():被多线程重入,进而导致线程安全的问题。
注意双判断空指针,降低锁冲突的概率,提高性能!
只需要改进GetInstance函数即可
static ThreadPool<T> *GetInstance()
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
if (ins == nullptr)// 双判定,减少锁的征用,提高效率
{
pthread_mutex_lock(&lock);
// 当前单例对象还没有被创建
if (ins == nullptr)
{
ins = new ThreadPool<T>();
ins->InitThreadPool();
std::cout << "首次加载对象" << std::endl;
}
pthread_mutex_unlock(&lock);
}
return ins;
}