Linux多线程:线程池

目录

一、线程池简介

二、应用场景

三、线程池的实现

1.创建一堆线程

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

3.定义任务处理方法

4.任务处理

四、代码实现

1.完整代码

2.实现效果

3.注意事项


一、线程池简介

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

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

二、应用场景

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

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

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

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

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

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

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

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

三、线程池的实现

1.创建一堆线程

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

3.定义任务处理方法

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

解决方案:

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

4.任务处理

四、代码实现

1.完整代码

#include
#include
#include
#include
#include

#define MAX_QUEUE 5
#define MAX_THREAD 5

//线程安全的任务队列
template 
class BlockQueue{
  private:
    int _capacity;//缓冲区容量
    std::queue _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 _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.实现效果

Linux多线程:线程池_第1张图片

 

3.注意事项

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

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

解决方法:

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

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

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

有问题的解决方案:

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

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

合适的解决方案:

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

你可能感兴趣的:(Linux操作系统,linux,c++)