线程池
一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能保证内核的充分利用。,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
应用场景
- 需要大量的线程来完成任务,且完成任务的时间比较短。WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。但对于长时间的任务,比如一个Telnet连接请求,线程池的优势就不明显了。因为Telnet会话时间比线程创建时间大多了。
- 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
- 接收突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量用户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存达到极限,出现错误。
示例
thread_pool.hpp
#pragma once
#include <iostream>
#include <unistd.h>
#include <queue>
#include <string>
#include <pthread.h>
namespace ns_thread_pool
{
const int g_num = 5;
template <class T>
class ThreadPool
{
private:
int _num;
pthread_mutex_t _mtx;
pthread_cond_t _cond;
std::queue<T> _tq; //临界资源
public:
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 _tq.empty();
}
public:
ThreadPool(int num = g_num)
: _num(num)
{
pthread_mutex_init(&_mtx, nullptr);
pthread_cond_init(&_cond, nullptr);
}
~ThreadPool()
{
pthread_mutex_destroy(&_mtx);
pthread_cond_destroy(&_cond);
}
static void *Routine(void *arg) //类内函数自带this指针,故使用static
{
pthread_detach(pthread_self());
ThreadPool<T> *tp = (ThreadPool<T>*)arg;
while (true)
{
tp->Lock();
while (tp->IsEmpty())
{
tp->Wait();
}
T task;
tp->Pop(&task);
tp->UnLock();
task();
}
}
void InitThreadPool()
{
pthread_t tid;
for (int i = 0; i < _num; i++)
{
pthread_create(&tid, nullptr, Routine, (void*)this);
}
}
void Push(const T& in)
{
Lock();
_tq.push(in);
UnLock();
Wakeup();
}
void Pop(T *out)
{
*out = _tq.front();
_tq.pop();
}
};
}
main.cc
#include "thread_pool.hpp"
#include "Task.hpp"
#include <iostream>
#include <unistd.h>
#include <time.h>
using namespace ns_task;
using namespace ns_thread_pool;
int main()
{
srand((long long)time(nullptr));
ThreadPool<Task> *tp = new ThreadPool<Task>();
tp->InitThreadPool();
while (true)
{
int x = rand() % 20 + 1;
int y = rand() % 10 + 1;
char op = ops[rand() % 5];
Task t(x, y, op);
tp->Push(t);
std::cout << "插入任务:" << x << op << y << "=?" << std::endl;
sleep(1);
}
return 0;
}
Task.hpp
#pragma once
#include <iostream>
#include <pthread.h>
#include <string>
namespace ns_task
{
std::string ops = "+-*/%";
class Task
{
private:
int _x;
int _y;
char _op;
public:
Task() {}
Task(int x, int y, char op)
: _x(x)
, _y(y)
, _op(op)
{}
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 << "no such op" << std::endl;
break;
}
std::cout << pthread_self() << "is dealing with " << _x << _op << _y << "=" << res << std::endl;
return res;
}
int operator()()
{
return Run();
}
~Task()
{}
};
}
单例模式
某些类,之应该具有一个对象(实例),就称之为单例。在很多服务器开发场景中,经常需要让服务器加载很多的数据到内存中,此时往往要用一个单例的类来管理这些数据。
饿汉实现方式和懒汉实现方式
饿汉方式:吃完饭,立刻洗碗,这样下一顿吃的时候立刻那碗吃饭
懒汉方式:等到下一顿再洗碗
懒汉方式的核心思想是“延时加载”,从而优化服务器的启动速度。
thread_pool.hpp
#pragma once
#include <iostream>
#include <unistd.h>
#include <queue>
#include <string>
#include <pthread.h>
namespace ns_thread_pool
{
const int g_num = 5;
template <class T>
class ThreadPool
{
private:
int _num;
pthread_mutex_t _mtx;
pthread_cond_t _cond;
std::queue<T> _tq; //临界资源
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=(const ThreadPool &tp) = delete;
public:
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 _tq.empty();
}
public:
static ThreadPool<T>* GetInstance(const int num = g_num)
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
if (ins == nullptr)
{
pthread_mutex_lock(&lock);
if (ins == nullptr)
{
std::cout << "首次调用" << std::endl;
ins = new ThreadPool<T>(num);
ins->InitThreadPool();
}
pthread_mutex_unlock(&lock);
}
return ins;
}
~ThreadPool()
{
pthread_mutex_destroy(&_mtx);
pthread_cond_destroy(&_cond);
}
static void *Routine(void *arg) //类内函数自带this指针,故使用static
{
pthread_detach(pthread_self());
ThreadPool<T> *tp = (ThreadPool<T>*)arg;
while (true)
{
tp->Lock();
while (tp->IsEmpty())
{
tp->Wait();
}
T task;
tp->Pop(&task);
tp->UnLock();
task();
}
}
void InitThreadPool()
{
pthread_t tid;
for (int i = 0; i < _num; i++)
{
pthread_create(&tid, nullptr, Routine, (void*)this);
}
}
void Push(const T& in)
{
Lock();
_tq.push(in);
UnLock();
Wakeup();
}
void Pop(T *out)
{
*out = _tq.front();
_tq.pop();
}
};
template <class T>
ThreadPool<T>* ThreadPool<T>::ins = nullptr;
}
main.cc
#include "thread_pool.hpp"
#include "Task.hpp"
#include <iostream>
#include <unistd.h>
#include <time.h>
using namespace ns_task;
using namespace ns_thread_pool;
int main()
{
srand((long long)time(nullptr));
while (true)
{
int x = rand() % 20 + 1;
int y = rand() % 10 + 1;
char op = ops[rand() % 5];
Task t(x, y, op);
ThreadPool<Task>::GetInstance()->Push(t);
std::cout << "插入任务:" << x << op << y << "=?" << ThreadPool<Task>::GetInstance() << std::endl;
sleep(1);
}
return 0;
}