Linux多线程:线程池

简介: Linux多线程:线程池

一、线程池简介


线程池:由一堆工作线程+一个线程安全的任务队列构成。


       外界将需要处理的任务,加入到线程安全的任务队列中,线程池中的工作线程不断的从任务队列中取出任务进行处理。


二、应用场景


应用场景:有大量数据请求,需要并发处理的场景。


       1)需要大量的线程来完成任务,且完成任务的时间较短;


       2)对性能要求苛刻的应用,比如要求服务器迅速响应客户请求;


       3)接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。


大量请求需要处理,若针对每一个请求创建一个线程,会存在以下问题:


       1)资源耗尽,系统崩溃风险


       2)在任务的并发处理中,执行流并不是越多越好


注:一个任务处理的总时间消耗 = 创建线程的时间 + 任务处理的时间 + 线程销毁的时间,则在任务处理中,大量的时间成本消耗在了线程的创建与销毁上。此时就可以选择应用线程池来进行任务处理。


三、线程池的实现


1.创建一堆线程


2.创建一个线程安全的任务队列


3.定义任务处理方法


       不同的客户端可能有不同的请求,不同的请求右不同的处理方式。


解决方案:


       让外界在传入请求的同时,附上此请求的对应处理方法,线程只需要根据请求和传入的处理方法去进行任务处理即可。


4.任务处理


四、代码实现


1.完整代码


#include<iostream>
#include<pthread.h>
#include<queue>
#include<cstdlib>
#include<unistd.h>
#define MAX_QUEUE 5
#define MAX_THREAD 5
//线程安全的任务队列
template <class T>
class BlockQueue{
  private:
    int _capacity;//缓冲区容量
    std::queue<T> _queue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond_pro;
    pthread_cond_t _cond_con;
  public:
    BlockQueue(int cap = MAX_QUEUE) : _capacity(cap) {
      pthread_mutex_init(&_mutex, NULL);
      pthread_cond_init(&_cond_pro, NULL);
      pthread_cond_init(&_cond_con, NULL);
    }
    ~BlockQueue() {
      pthread_mutex_destroy(&_mutex);
      pthread_cond_destroy(&_cond_pro);
      pthread_cond_destroy(&_cond_con);
    }
    bool Push(const T &data) {
      pthread_mutex_lock(&_mutex);
      while (_queue.size() == _capacity) {
        pthread_cond_wait(&_cond_pro, &_mutex);
      }
      _queue.push(data);
      pthread_cond_signal(&_cond_con);
      pthread_mutex_unlock(&_mutex);
    }
    bool Pop(T *data) {
      pthread_mutex_lock(&_mutex);
      while (_queue.empty()) {
        pthread_cond_wait(&_cond_con, &_mutex);
      }
      *data = _queue.front();
      _queue.pop();
      pthread_cond_signal(&_cond_pro);
      pthread_mutex_unlock(&_mutex);
    }
};
//任务处理方法
typedef void (*handler_t)(int data);
class ThreadTask {
  private:
    int _data;
    handler_t _handler;
  public:
    ThreadTask() {}
    ThreadTask(int data, handler_t handler)
      :_data(data)
       ,_handler(handler)
  {}
    void Run() {
      _handler(_data);
    }
};
//线程池
class ThreadPool {
  private:
    int thread_count;
    BlockQueue<ThreadTask> _queue;
    //这里需要设置为静态成员函数,否则参数多一个this指针,无法匹配
    static void *Worker(void *arg) {
      //不断取出任务并处理
      ThreadPool *pool = (ThreadPool*)arg;
      while (1) {
        ThreadTask task;
        pool->_queue.Pop(&task);
        task.Run();
      }
    }
  public:
    ThreadPool(int tcount = MAX_THREAD, int qcount = MAX_QUEUE)
      :thread_count(tcount)
       ,_queue(qcount)
  {
    int ret;
    pthread_t tid;
    //创建工作线程
    for (int i = 0; i < tcount; ++i) {
      ret = pthread_create(&tid, NULL, Worker, this);
      if (ret != 0) {
        printf("Create thread error!\n");
        exit(0);
      }
      pthread_detach(tid);//将线程分离,不关心其退出
    }
  }
    bool Push(const ThreadTask &task) {
      _queue.Push(task);
    }
};
//处理方法
void Conduct(int data) {
  printf("Thread: %p --- sleep %d seconds!\n", pthread_self(), data);
  sleep(data % 4 + 1);
}
int main() {
  ThreadPool pool;
  for (int i = 0; i < 20; ++i) {
    ThreadTask task(i, Conduct);
    pool.Push(task);
  }
  while (1) sleep(1);//防止程序退出
  return 0;
}


2.实现效果

1.png


3.注意事项


1)线程入口函数类型不匹配:


       若入口函数是一个类的成员函数,那么参数列表会有一个隐藏的this指针,导致入口函数类型不匹配,


解决方法:


       可将线程的入口函数设置为静态成员函数(静态成员函数,不含this指针)。


2)静态成员函数无法直接访问类的普通成员变量:


       静态成员函数在调用时,因为没有传入this指针,无法直接访问类的普通成员变量。


有问题的解决方案:


       将入口函数中访问类的成员变量(阻塞队列),设置为静态。


       存在问题:若将类的成员变量(阻塞队列)设置为静态,则类实例化的所有对象共用同一个静态成员变量(阻塞队列),所以不可取。


合适的解决方案:


       在创建线程池时,将线程池对象的this指针传入到线程的入口函数中,通过this指针,来访问其对应的成员变量(阻塞队列)。


相关文章
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
587 0
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
设计模式 缓存 安全
【JUC】(6)带你了解共享模型之 享元和不可变 模型并初步带你了解并发工具 线程池Pool,文章内还有饥饿问题、设计模式之工作线程的解决于实现
JUC专栏第六篇,本文带你了解两个共享模型:享元和不可变 模型,并初步带你了解并发工具 线程池Pool,文章中还有解决饥饿问题、设计模式之工作线程的实现
783 2
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
903 60
【Java并发】【线程池】带你从0-1入门线程池
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
549 67
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
934 5
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
Java
线程池是什么?线程池在实际工作中的应用
总的来说,线程池是一种有效的多线程处理方式,它可以提高系统的性能和稳定性。在实际工作中,我们需要根据任务的特性和系统的硬件能力来合理设置线程池的大小,以达到最佳的效果。
429 18
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
683 20
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
392 17

热门文章

最新文章