一种线程使用模式,线程过多会带来调度开销,进而影响缓存局部性和整体性能。线程池维护多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价,线程池不仅能保证内核的充分利用,还能防止过分调度,可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量
线程池的应用场景:
1.需要大量的线程来完成任务,且完成任务的时间比较短。WEB服务器完成网页请求这样的任务,使用线程池是非常合适的。因为单个任务小,任务数量巨大,可以想象一个热门网站的点击次数。但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为telnet会话时间比线程的创建时间大多了
2.对性能要求苛刻的应用,比如要求服务器迅速响应客户请求
3.接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间产生大量线程可能使内存达到极限,出现错误
提前申请很多线程,线程处于睡眠状态,当往共享区放入任务后唤起线程拿到任务开始运行。和生产消费类似
类的设计
用一个数组保存所有线程,元素类型是自定义线程类,包含线程tid和名字。队列作为共享数据。一个锁保护共享数据的安全,条件变量保证多个线程访问的同步。start功能创建指定数量的线程,运行指定函数,类内函数默认第一个参数是this指针,但线程函数必须是void*参数,所以这个函数加static。加锁访问,当共享资源为空时线程挂起,否则取出队首的元素交给线程执行,执行前释放锁。放入任务功能,加锁访问,放入成功后唤醒休眠线程
#pragma once
#include <vector>
#include <queue>
#include <pthread.h>
#include <string>
#include <unistd.h>//换为封装的线程
struct ThreadInfo
{pthread_t _tid;std::string _name;
};
template <class T>
class pool
{static const int defaultnum = 5;public:pool(int num = defaultnum):_thread(num){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);}std::string getname(pthread_t tid){for (auto ch : _thread){if (ch._tid == tid){return ch._name;}}return "None";}static void* HandlerTask(void* args){pool<T> *tp = static_cast<pool<T> *>(args);std::string name = tp->getname(pthread_self());while (true){pthread_mutex_lock(&(tp->_mutex));while (tp->_que.empty()){pthread_cond_wait(&(tp->_cond), &(tp->_mutex));}T t = tp->_que.front();tp->_que.pop();pthread_mutex_unlock(&tp->_mutex);t.run();printf("%s finsih task:%s\n", name.c_str(), t.getresult().c_str());sleep(1);}}void start(){for (int i = 0; i < _thread.size(); i++){_thread[i]._name = "thread" + std::to_string(i);pthread_create(&_thread[i]._tid, nullptr, HandlerTask, this);}}void push(const T& x){pthread_mutex_lock(&_mutex);_que.push(x);pthread_cond_signal(&_cond);pthread_mutex_unlock(&_mutex);}~pool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}
private:std::vector<ThreadInfo> _thread;std::queue<T> _que;pthread_mutex_t _mutex;pthread_cond_t _cond;
};
task.hpp
#pragma once
#include <stdio.h>
#include <string>enum
{DIVZERO = 1,UNKNOW
};
std::string g_op = "+-*/";
struct task
{
public:task(){}task(int a, int b, char op):_a(a), _b(b), _op(op), _result(0), _exitcode(0){}void run(){switch(_op){case '+':_result = _a + _b;break;case '-':_result = _a - _b;break;case '*':_result = _a * _b;break;case '/':if (_b == 0){_exitcode = DIVZERO;}else{_result = _a / _b;} break;default:_exitcode = UNKNOW;break;}//printf("%d+%d结果:%d\n", _a, _b, _a + _b);}std::string getresult(){std::string str = std::to_string(_a) + _op + std::to_string(_b);str += "=";str += std::to_string(_result);str += " [exit:";str += std::to_string(_exitcode);str += "]";return str;}std::string gettask(){std::string str = std::to_string(_a) + _op + std::to_string(_b);return str;}private:int _a;int _b;char _op;int _result;int _exitcode;
};
main.cc
#include <stdlib.h>
#include <thread>
#include "task.hpp"
#include "ThreadPool.hpp"
#include "Thread.hpp"int main()
{srand(time(NULL));printf("run\n");pool<task> *pl = new pool<task>(5);pl->start();sleep(3);while (true){ // 构建任务int data1 = rand() % 10;int data2 = rand() % 10 + 1;char op = g_op[rand() % 4];task t(data1, data2, op);// 交给线程池printf("task:%s\n", t.gettask().c_str());pl->push(t);sleep(1);}
}
线程类封装
c++11引入了线程类,对底层线程函数做了封装,仍然离不开lpthread库,对原生线程也试着封装,引入自己的功能
线程类保存tid,名字,是否运行,启动时间,需要调用的函数
通过函数指针设置为类成员变量需要调用的函数,生成的线程先调用接口函数,接口函数调用入口函数,入口函数调用函数指针指向的函数。可以通过类模板作为成员变量来引入需要传入的参数
#pragma once
#include <string>
#include <ctime>
#include <pthread.h>static int num = 1;
typedef void (*callback)();
//模板传参数
class Thread
{
public:Thread(callback cb):_tid(0), _name(""), _time(0), _running(false), _cb(cb){}static void* routine(void* args){Thread *tp = static_cast<Thread *>(args);tp->entry();return nullptr;}void run(){_name = "thread-" + std::to_string(num++);_time = time();_running = true;pthread_create(&_tid, nullptr, routine, this);}void join(){pthread_join(_tid, nullptr);}//传入参数void entry(){_cb();}std::string name(){return _name;}pthread_t gettid(){return _tid;}uint64_t time(){return _time;}bool isrunning(){return _running;}private:pthread_t _tid;std::string _name;uint64_t _time;bool _running;callback _cb;
};