线程池4cpp

线程池

思想

线程池的核心思想相对于one TcpConnection per thread来说。有如下优点。

  1. 频繁创建和消耗线程开销很大,如果业务很短,开销比创建线程还小,那么就得不偿失。线程池采用固定大小的线程数,减少了这部分消耗。
  2. 大并发会导致起非常多的线程,从而吃光内存。
  3. 多线程并非很多线程就很好。实际上和CPU的核心数有关。

API

class ThreadPool
{
public:
    ThreadPool(int threads, int MaxTasks);
    bool addTask(const Task&);
    void destroy();
    ~ThreadPool();
};

个人认为有如上的API使用足以。

任务类

线程池的一个关键是抽象人物类
我们有很多种做法

  1. 类模板
  2. 利用bind/functiona采用基于对象的方式
  3. 利用多态

个人比较偏好第二种做法,后面会简单介绍几种做法。先看下如何传递线程函数入口

如何传递线程函数的入口

这里简单说明下
由于pthread_create()传递给它的只能是静态函数,我们一般会设置一个
static void* threadFunc(void* arg)这样的静态成员函数。
因此这个静态成员函数被我们作为线程函数的入口。而且它一般都是写死的。

void*
ThreadPool::threadFunc(void *arg)
{
    ThreadPool * pool = static_cast<ThreadPool*> arg;
    pool->run();
    return pool;
}

这就需要我们在创建线程的时候交给线程函数this指针。即
pthread_create(&threadId, NULL, threadFunc, this);

第一种做法:采取模板的做法

template <typename T>
class ThreadPool
{
public:
...
private:
    run();
private:
    queue<Task*> tasks_;
}

第二种做法:采取function的做法

class ThreadPool
{
    typedef function<void()> Task;
private:
    queue<Task> tasks_;
}

第三种做法:采取继承的方式
需要新增加一个Task虚基类

class Task
{
public:
    virtual process() = 0;
    virtual ~Task() = default;
}

说白了,这里的核心人物就是为了抽象任务类。从而引发了三种不同的方式来抽象任务类。

任务队列——BlockingQueue

接下来的关键实际上就是实现一个阻塞队列。
阻塞队列可以有三个API,需要用到条件变量和锁的结合

扫描二维码关注公众号,回复: 6656961 查看本文章
template <typename T>
class BlockingQueue
{
public:
    ...
    bool put();
    T get();
    size_t size()const;
    
private:
    MutexLock mutex_;
    Condition cond_;
}

BlockingQueue是一个线程安全的阻塞队列。当队列为空时,如果我们要拿取任务,该线程会阻塞在这里,直到有其它线程向里面添加任务为止。

实现

github

猜你喜欢

转载自blog.csdn.net/weixin_43468441/article/details/90900275