【Linux】线程池的简易实现(懒汉模式)
- 游戏开发
- 2025-08-05 04:30:01

文章目录 前言一、懒汉方式1.普通模式1.线程安全模式 二、源代码1.Task.hpp(要执行的任务)2.ThreadPool.hpp(线程池)3.Main.cpp
前言
`
线程池: 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
线程池的应用场景: 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技 术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个 Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情 况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限, 出现错误. 一、懒汉方式 1.普通模式懒汉方式实现单例模式
template <typename T> class Singleton { static T* inst; public: static T* GetInstance() { if (inst == NULL) { inst = new T(); } return inst; } }; 1.线程安全模式存在一个严重的问题, 线程不安全. 第一次调用 GetInstance 的时候, 如果两个线程同时调用, 可能会创建出两份 T 对象的实例. 但是后续再次调用, 就没有问题了.
// 懒汉模式, 线程安全 template <typename T> class Singleton { volatile static T* inst; // 需要设置 volatile 关键字, 否则可能被编译器优化. static std::mutex lock; public: static T* GetInstance() { if (inst == NULL) { // 双重判定空指针, 降低锁冲突的概率, 提高性能. lock.lock(); // 使用互斥锁, 保证多线程情况下也只调用一次 new. if (inst == NULL) { inst = new T(); } lock.unlock(); } return inst; } }; 注意事项: 1. 加锁解锁的位置 2. 双重 if 判定, 避免不必要的锁竞争 3. volatile关键字防止过度优化 二、源代码 1.Task.hpp(要执行的任务)可根据自己的需求修改要执行的任务,这里我们以进行两个数的加减乘除为例子
#pragma once #include <iostream> #include <string> std::string opers = "+-*/%";//运算符号 enum//返回值 { DivZero = 1, ModZero, Unknown }; class Task { public: Task() { } Task(int x, int y, char op) : data1_(x), data2_(y), oper_(op), result_(0), exitcode_(0) { } void run() { switch (oper_) { case '+': result_ = data1_ + data2_; break; case '-': result_ = data1_ - data2_; break; case '*': result_ = data1_ * data2_; break; case '/': { if (data2_ == 0) exitcode_ = DivZero; else result_ = data1_ / data2_; } break; case '%': { if (data2_ == 0) exitcode_ = ModZero; else result_ = data1_ % data2_; } break; default: exitcode_ = Unknown; break; } } void operator()()//直接使用仿函数,调用run方法 { run(); } std::string GetResult() { std::string r = std::to_string(data1_); r += oper_; r += std::to_string(data2_); r += "="; r += std::to_string(result_); r += "[code: "; r += std::to_string(exitcode_); r += "]"; return r; } std::string GetTask() { std::string r = std::to_string(data1_); r += oper_; r += std::to_string(data2_); r += "=?"; return r; } ~Task() { } private: int data1_; int data2_; char oper_; int result_; int exitcode_; }; 2.ThreadPool.hpp(线程池) #pragma once #include <iostream> #include <vector> #include <string> #include <queue> #include <pthread.h> #include <unistd.h> struct ThreadInfo//线程信息 { pthread_t tid; std::string name; }; static const int defalutnum = 5;//默认创建线程个数 template <class T> class ThreadPool { public: void Lock() { pthread_mutex_lock(&mutex_); } void Unlock() { pthread_mutex_unlock(&mutex_); } void Wakeup() { pthread_cond_signal(&cond_); } void ThreadSleep() { pthread_cond_wait(&cond_, &mutex_); } bool IsQueueEmpty() { return tasks_.empty(); } std::string GetThreadName(pthread_t tid) { for (const auto &ti : threads_) { if (ti.tid == tid) return ti.name; } return "None"; } public: static void *HandlerTask(void *args) //这里需要用静态函数,因为在类中默认隐藏一个this指针 { //这里传args直接把ThreadPool这个类传入进来进行操作 //这样既能访问类中的方法也能符合传入的参数个数为1个的规则 ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args); std::string name = tp->GetThreadName(pthread_self()); while (true) { tp->Lock(); while (tp->IsQueueEmpty()) //循环判断,防止线程的伪唤醒 { tp->ThreadSleep(); } T t = tp->Pop(); tp->Unlock(); t();//直接调用任务的处理方法(仿函数) std::cout << name << " run, " << "result: " << t.GetResult() << std::endl; } } void Start()//启动线程池 { int num = threads_.size(); for (int i = 0; i < num; i++) { //创建线程并且给线程池命名 threads_[i].name = "thread-" + std::to_string(i + 1); pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this); } } T Pop() { T t = tasks_.front(); tasks_.pop(); return t; } void Push(const T &t) { Lock(); tasks_.push(t); Wakeup();//队列中有了数据 //可能在之前任务为空的时候有线程进入了休眠 //所以放入任务后,进行一次唤醒操作 Unlock(); } static ThreadPool<T> *GetInstance() { if (nullptr == tp_) // 双重判定空指针, 降低锁冲突的概率, 提高性能. { pthread_mutex_lock(&lock_); //这个lock_锁为静态成员 if (nullptr == tp_) { std::cout << "log: singleton create done first!" << std::endl; tp_ = new ThreadPool<T>(); } pthread_mutex_unlock(&lock_); } return tp_; } private: //因为为懒汉单例模式,只有一个ThreadPool类,所以为了避免 //冲突,把构造函数,拷贝构造函数都设置成私有禁用 ThreadPool(int num = defalutnum) : threads_(num) { pthread_mutex_init(&mutex_, nullptr); pthread_cond_init(&cond_, nullptr); } ~ThreadPool() { pthread_mutex_destroy(&mutex_); pthread_cond_destroy(&cond_); } ThreadPool(const ThreadPool<T> &) = delete; const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b=c private: std::vector<ThreadInfo> threads_; std::queue<T> tasks_; pthread_mutex_t mutex_; pthread_cond_t cond_; static ThreadPool<T> *tp_;//懒汉单例模式所创建的 //唯一的ThreadPool类 static pthread_mutex_t lock_; //静态方法只能访问类中的静态成员,所以还需要一个静态锁在 //获取这个对象的时候使用(GetInstance) }; //静态成员在类外进行初始化 template <class T> ThreadPool<T> *ThreadPool<T>::tp_ = nullptr; template <class T> pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER; 3.Main.cpp #include <iostream> #include <ctime> #include "ThreadPool.hpp" #include "Task.hpp" pthread_spinlock_t slock; int main() { std::cout << "process runn..." << std::endl; sleep(3); //GetInstance()获取单例对象 ThreadPool<Task>::GetInstance()->Start(); srand(time(nullptr) ^ getpid()); while(true) { //1. 构建任务 int x = rand() % 10 + 1; usleep(10); int y = rand() % 5; char op = opers[rand()%opers.size()]; Task t(x, y, op); ThreadPool<Task>::GetInstance()->Push(t); //2. 交给线程池处理 std::cout << "main thread make task: " << t.GetTask() << std::endl; sleep(1); } }【Linux】线程池的简易实现(懒汉模式)由讯客互联游戏开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【Linux】线程池的简易实现(懒汉模式)”