目录
一、单列模式
1.1 单列模式概念以及实现条件
1.2 饿汉模式
1.1.1 饿汉模式代码实现
1.1.2 饿汉模式特征和优缺点
1.3 懒汉模式
1.3.1 懒汉模式代码实现
1.3.2 懒汉模式特征以及优缺点
二、线程池
2.1 线程池概念
2.2 实现简单线程池逻辑
2.3 模拟实现懒汉模式线程池
2.3.1 mutex.hpp 封装锁
2.3.2 Task.hpp 任务封装
2.3.3 Thread.hpp 线程封装
2.3.4 ThreadPool.hpp 线程池封装
2.3.5 main.cc 上层代码
2.3.6 执行结果展示
2.4 线程池应用场景
单例模式主要确保一个类只有一个实例!也就是对象唯一。其中饿汉、懒汉模式是单列模式的一种。
使对象唯一的条件:
class Singleton {
public:
//静态成员函数--》不需要this指针
static Singleton& getInstance() {
static Singleton instance; //静态成员对象--》生命周期长
return instance;
}
// 防止拷贝构造函数和赋值运算符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() {} //构造函数私有化
};
特征:
- 在类加载时就创建唯一实例,保证了一个类只有一个实例。
- 实现起来比较简单,没有多线程同步问题。
优点:
- 由于实例在类加载时就被创建,因此不会造成资源的浪费。
- 没有多线程同步问题,保证了线程安全。
缺点:
- 如果该类从未被使用,那么它的实例对象就会被创建并一直占用内存,即使该实例对象可能永远不会被使用。
#include
//懒汉模式 main函数之后创建对象
class InfoSingleton
{
public:
static InfoSingleton& GetInstance()
{
//双重检查 最外面if可以保证不会每次都加锁判断
if (_psins == nullptr)
{
LockGuard lock(_smtx);
if (_psins == nullptr)
{
_psins = new InfoSingleton;
}
}
return *_psins;
}
private:
InfoSingleton() {}
//封死拷贝赋值构造
InfoSingleton(const InfoSingleton& s) = delete;
InfoSingleton& operator=(const InfoSingleton& s) = delete;
static InfoSingleton* _psins;
static mutex _smtx;
};
InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;
特征:
懒汉模式是一种单例模式,它的特点是在第一次使用实例对象时,才创建对象。
优点:
- 在第一次使用实例对象时,创建对象进程启动无负载。也就是说,如果单例对象构造十分耗时或者占用很多资源,如加载插件、初始化网络连接、读取文件等等,而有可能该对象在程序运行时并不会被使用,那么在程序一开始就进行初始化会导致程序启动时非常缓慢,使用懒汉模式(延迟加载)则能避免这种情况。
缺点:
- 懒汉模式相对于饿汉模式来说,实现上更复杂一些,因为涉及到线程安全问题。
线程池是一种多线程处理形式,它预先将任务添加到队列中,然后在创建线程后自动启动这些任务。线程池中的线程都是后台线程,每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。
线程池的工作原理如下:
RAII的思想,通过类生命周期来加锁解锁!
#include
#include
class Mutex
{
public:
Mutex(pthread_mutex_t* lock_p = nullptr):lock_p_(lock_p)
{}
void lock()
{
if(lock_p_)
pthread_mutex_lock(lock_p_);
}
void unlock()
{
if(lock_p_)
pthread_mutex_unlock(lock_p_);
}
private:
pthread_mutex_t* lock_p_;
};
class LockGuard
{
public:
LockGuard(pthread_mutex_t* mutex):mutex_(mutex)
{
mutex_.lock();
}
~LockGuard()
{
mutex_.unlock();
}
private:
Mutex mutex_;
};
#pragma once
#include
#include
#include
#include
// 任务对象
class Task
{
public:
//==typedef
using func_t = std::function;
Task() {}
Task(func_t callback, int x = 0, int y = 0, char op = '+') : _x(x), _y(y), _op(op), _callback(callback)
{
}
// 仿函数
std::string operator()()
{
double ret = _callback(_x, _y, _op);
char buffer[64];
snprintf(buffer, sizeof buffer, "%d %c %d = %lf", _x, _op, _y, ret);
return buffer;
}
private:
int _x;
int _y;
char _op;
func_t _callback; // 回调函数
};
//处理数据函数
double calculator(int x, int y, char op)
{
double ret = 0.0;
switch (op)
{
case '+':
ret = x + y;
break;
case '-':
ret = x - y;
break;
case '*':
ret = x * y;
break;
case '/':
if (y == 0)
ret = 0;
else
ret = (double)x / y;
break;
case '%':
if (y == 0)
ret = 0;
else
ret = x % y;
break;
default:
break;
}
return ret;
}
#pragma once
#include
#include
#include
#include
#include
using func_t = std::function;
namespace MyThread
{
class Thread
{
public:
Thread(func_t callback, void *args = nullptr) : _callback(callback), _args(args)
{
char namebuffer[64];
snprintf(namebuffer, sizeof namebuffer, "thread %d ", ++threadNum);
_name = namebuffer;
}
void start()
{
int n = pthread_create(&_tid, nullptr, start_route, (void *)this);
assert(n == 0);
(void)n;
}
void join()
{
pthread_join(_tid, nullptr);
}
std::string ThreadName()
{
return _name;
}
void *callback()
{
return _callback(_args);
}
private:
//在类中调用线程执行流函数必须要static-->因为默认的函数是只有一个void*参数,如果没有static,会默认带有隐藏参数this!
//仅仅使用stati后,还要考虑如何访问类成员变量\函数-->传入的void*参数是this!
static void *start_route(void *args)
{
Thread *pt = static_cast(args);
return pt->callback();
}
private:
pthread_t _tid; //线程ID
void *_args; //线程所拥有的资源
std::string _name; //线程名称
func_t _callback; //回调函数
static int threadNum;//线程对象个数
};
int Thread::threadNum = 0;
}
#pragma once
#include "Thread.hpp"
#include "mutex.hpp"
#include "Task.hpp"
#include
#include
#include
#include
using namespace MyThread;
static const int gnum = 5;
template
class ThreadPool
{
//和Thread.hpp中的理由一样 static防this指针 args=this
static void *Handeler(void *args)
{
while (true)
{
T t;
ThreadPool *Self = static_cast *>(args);
{
LockGuard lock(&Self->_mutex);
while (Self->_TaskQueue.empty())
{
pthread_cond_wait(&Self->_cond, &Self->_mutex);
}
t = Self->_TaskQueue.front();
Self->_TaskQueue.pop();
}
std::cout << "线程[" << pthread_self() << "]处理完了一个任务: " << t() << std::endl;
}
return nullptr;
}
//构造函数私有 初始化创建一批线程
ThreadPool(const int num = gnum) : _num(num)
{
pthread_mutex_init(&_mutex, nullptr);
pthread_cond_init(&_cond, nullptr);
for (int i = 0; i < _num; i++)
{
_threads.push_back(new Thread(Handeler, this));
}
}
//拷贝赋值封死
ThreadPool(const ThreadPool &) = delete;
void operator=(const ThreadPool &) = delete;
public:
// 懒汉模式 延迟加载
static ThreadPool *GetInstance()
{
if (_tp == nullptr)
{
_singleton_lock.lock();
if (_tp == nullptr)
_tp = new ThreadPool();
_singleton_lock.unlock();
}
return _tp;
}
//执行线程
void run()
{
for (const auto &t : _threads)
{
t->start();
std::cout << t->ThreadName() << "starts..." << std::endl;
}
}
//放数据接口
void push(const T &in)
{
LockGuard lock(&_mutex);
_TaskQueue.push(in);
pthread_cond_signal(&_cond);
}
~ThreadPool()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
for (const auto &t : _threads)
delete t;
}
private:
std::vector _threads; //数组管理线程
int _num;
std::queue _TaskQueue; //任务队列
pthread_mutex_t _mutex;//互斥
pthread_cond_t _cond;// 同步
//单列模式
static ThreadPool *_tp;
static std::mutex _singleton_lock;
};
template
ThreadPool *ThreadPool::_tp = nullptr;
template
std::mutex ThreadPool::_singleton_lock;
#include "ThreadPool.hpp"
#include
#include
int main()
{
srand((unsigned long)time(nullptr) ^ getpid());
//初始化并运行线程
ThreadPool::GetInstance()->run();
sleep(2);
while (true)
{
//1.获取任务
const char *str = "+-*/%";
int x = rand() % 10 + 1;
int y = rand() % 5 + 1;
char op = str[rand() % 5];
Task t(calculator, x, y, op);
//2.放入任务至队列中
ThreadPool::GetInstance()->push(t);
std::cout << "生产任务: " << x << " " << op << " " << y << " = ?" << std::endl;
sleep(1);
}
while (true)
{
sleep(1);
}
return 0;
}
线程池主要应用在以下场景中:
此外,线程池在以下情况中也可以发挥出其优势:
总的来说,线程池主要应用在需要对并发执行的任务进行高效、有序、可控制管理的场景中。