一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着 监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利 用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。2.模拟实现
ThreadPool.hpp
#pragma once #include#include #include #define NUM 5 template class ThreadPool { private: int num; std::queue task_queue; //任务队列,临界资源 pthread_mutex_t lock; //对临界资源进行保护 pthread_cond_t cond; //有休眠就有唤醒 public: ThreadPool(int _num = NUM):thread_num(_num) { pthread_mutex_init(&lock); //对锁初始化 pthread_cond_init(&cond,nullptr); } void LockQueue() { pthread_mutex_lock(&lock); //加锁 } void UnlockQueue() { pthread_mutex_unlock(&lock); //解锁 } bool IsQueueEmpty() { return task_queue.size() == 0 ? true : false; } void Wait() { pthread_cond_wait(&cond,&lock); } void WakeUp() { pthread_cond_signal(&cond); } static void *Routine(void *arg) //static:这个方法属于类,不属于对象,只会传入所想要传入的参数,不会有this指针 { pthread_detach(pthread_self()); ThreadPool *self = (ThreadPool*)arg; while(true) { self->LockQueue(); //为了访问非静态成员函数 while(self()->IsQueueEmpty()) { //wait self->Wait(); } //任务队列一定有队列 T t; self->Pop(); //拿任务 self->UnlockQueue(); //处理任务 t.Run(); } } void InitThreadPool() { pthread_t tid; for(int i = 0; i < thread_num; i++) { pthread_create(&tid,nullptr,Routine,this); pthread_detach(tid); } } void Push(const T& in) { LockQueue(); task_queue.push(in); UnlockQueue(); WakeUp(); } void Pop(T &out) { out = task_queue.front(); task_queue.pop(); } ~ThreadPool() { pthread_cond_destroy(&cond); pthread_mutex_destroy(&lock); } }
task.hpp
#pragma once #include#include //typedef int (*handler_t)(int,int,char); //回调函数 class Task { private: int x; int y; char op; //handler_t handler; public: Task() {} Task(int _x,int _y,chat _op):x(_x),y(_y),op(_op) { } void Run() { //任务处理 //int ret = handler(x,y,op); int z = 0; swotch(op) { case '+': z = x + y; break; case '-': z = x - y; break; case '*': z = x * y; break; case '/': if(y == 0) std::cerr << "div zero! " << std::endl; if(y != 0) z = x / y; break; case '%': if(y == 0) std::cerr << "mod zero! " << std::endl; if(y != 0) z = x % y; break; default: perror("operator error!n"); break; } std::cout << "thread:[" << pthread_self() << "]:" << x << op << y << "="<< z << std::endl; } ~Task() {} }
main.cc
#include"Task.hpp" #include"ThreadPool.hpp" #include二、线程安全的单例模式 1.设计模式#include #include int main() { ThreadPool *tp = new ThreadPool (); tp->InitThreadPool(); srand((unsigned long)time(nullptr)); const char *op = "+-*/%"; while(true) { int x = rand()%100+1; //[0.100] int y = rand()%100+1; Task t(x,y,op[x%5]); //x%5对应0-4 tp->Push(t); //任务塞队列 sleep(1); } return 0; }
对于常见的场景,给定对应的解决方案
2.单例模式(1)特点
某些类,只应该具有一个对象(实例),就称之为单例
(2)实现方式
饿汉实现方式
templateclass Singleton { static T data; //对象已经存在 public: static T* GetInstance() { return &data; } };
懒汉实现方式核心思想:“延时加载”,从而优化服务器的启动速度
templateclass Singleton { static T* inst; public: static T* GetInstance() { if(inst == NULL) { inst = new T(); } return inst; } };
存在问题:线程不安全,第一次调用Getinstance的时候,如果两个线程同时调用,可能会创建两份T对象的实例,但是后续再次调用,就没有问题了
改进:线程安全版本
templateclass Singleton { volatile static T* inst; //设置volatile,否则可能会被编译器优化 static std::mutex lock; public: static T* GetInstance() { if(inst == NULL) //双重判定空指针,降低锁冲突的概率,提高性能 lock.lock(); //使用互斥锁,保证多线程情况下也只能调用一次 { if(inst == NULL) { inst = new T(); } lock.unlock(); } return inst; } };
注意:
(1)加锁解锁的位置
(2)双重if判定,避免不必要的锁竞争
(3)volatile关键字防止过度优化
三种关系:读者和读者(没有冲突),写者和写者(互斥关系),读者和写者(互斥,同步)
读和写
一个交易场所
(1)数据写下之后,剩下操作就是读取
(2)写的操作少,读操作多
(1)设置读写优先
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
(2)初始化
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
(3)销毁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
(4)加锁和解锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
多线程情况下,读写同时到来,如果还有人在读,接下来的读的角色,不要进入临界区进行读,等写着进入,并写完,然后你再读(写者优先);反之就是读者优先



