线程池原理及实现

线程池是预先创建一组线程,并维持一个固定的数量,在需要的时候可以直接提供给程序使用,而不是让程序自行创建和销毁进程,避免了在运行中线程创建和销毁的开销。

1 线程池的构成

生产者线程: 发布任务,将任务放入到消息队列中

队列: 存放了发布的任务,包括任务执行的函数,任务的上下文(参数)

线程池(消费者): 线程池中的线程取出队列中的任务,依次执行

2 线程池的作用

为什么需要线程池?

某些任务特别耗时,严重影响主线程处理其他任务,可以将这些耗时任务放在其他线程异步执行。

耗时任务主要有两类:耗时等待(IO耗时)、耗时处理(CPU耗时)

线程池作用:

  • 复用线程资源:线程在使用完后并不是立马销毁,而是仍在线程池中,供下一次使用

  • 减少线程创建和销毁的开销:提前创建线程,并在最后统一销毁线程

  • 可异步处理生产者线程的任务:对于某个耗时的任务,不要让其在核心线程执行,而是为其分配一个线程执行

  • 并发执行:充分利用多核,并发执行任务

3 线程池数量

线程池的数量通常是固定的,因为即使线程池的数量持续增加,由于系统资源是有限的,并不能带来提升,反而由于线程调度带来了更多开销。

固定的线程池数量可以避免频繁创建线程和销毁线程。

按照经验,对于CPU密集型的情况,线程池个数即为CPU核心数,对于IO密集型,则为(线程等待时间 + CPU运算时间)* CPU核心数 / CPU运算时间。

4 应用场景-Nginx

Nginx作为静态服务器时,客户发起请求时,静态资源可直接从Nginx返回。

这些文件一般是存在Nginx本地,因此需要从本地加载进内存中,再返回给客户。

把文件加载进内存的操作不应该由主线程操作,而是使用另外一个线程操作。

流程:
主线程将任务放到一个消息队列中,线程池中的线程取出消息队列中的任务,进行处理,处理完成后放入完成消息队列,提供管道通知主线程,主线程从完成消息队列中取出结果。

在这里插入图片描述

5 线程池的实现

5.1 数据结构

任务结构:

typedef struct task_s {
	void *next;
	handler_pt func; //任务执行的函数
	void *arg; //参数
} task_t;
typedef void (*handler_pt)(void * /* ctx */);

任务队列:

typedef struct task_queue_s {
    void *head; // 头指针
    void **tail; // 尾指针
    int block; // 是否阻塞
    spinlock_t lock;
    pthread_mutex_t mutex; // 互斥锁
    pthread_cond_t cond; // 条件变量
} task_queue_t;

尾指针为void **tail,而任务结构体中第一个是void *next,tail指向了最后一个任务的next指针,范围为8个字节。

在这里插入图片描述

线程池:

struct thrdpool_s {
    task_queue_t *task_queue; //任务队列(阻塞队列)
    atomic_int quit;  // 退出 原子变量
    int thrd_count;
    pthread_t *threads;
};

5.2 相关函数

5.2.1 创建线程池

创建线程:

static int 
__threads_create(thrdpool_t *pool, size_t thrd_count) {
	pthread_attr_t attr;
	int ret;
	ret = pthread_attr_init(&attr); //线程独有的属性
	
	if (ret == 0) {
        pool->threads = (pthread_t *)malloc(sizeof(pthread_t) * thrd_count); //分配资源
        if (pool->threads) {
            int i = 0;
            for (; i < thrd_count; i++) {
                if (pthread_create(&pool->threads[i], &attr, __thrdpool_worker, pool) != 0) { //创建线程
                    break;
                }
            }
            pool->thrd_count = i;
            pthread_attr_destroy(&attr);
            if (i == thrd_count)
                return 0;
            __threads_terminate(pool); //回滚代码
            free(pool->threads); 
        }
        ret = -1;
    }
}

__thrdpool_worker函数:

static void *
__thrdpool_worker(void *arg)
{
	thrdpool_t *pool = (thrdpool_t*) arg;
    task_t *task;
    void *ctx;

	while (atomic_load(&pool->quit) == 0) {
        task = (task_t*)__get_task(pool->task_queue);
        if (!task) break;
        handler_pt func = task->func;
        ctx = task->arg;
        free(task);
        func(ctx);
    }

	return NULL;
}

销毁线程:

static void 
__threads_terminate(thrdpool_t * pool) {
    atomic_store(&pool->quit, 1);
    __nonblock(pool->task_queue);
    int i;
    for (i=0; i<pool->thrd_count; i++) {
        pthread_join(pool->threads[i], NULL);
    }
}

创建任务队列:

static task_queue_t *
__taskqueue_create() {
    int ret;
    task_queue_t *queue = (task_queue_t *)malloc(sizeof(task_queue_t));
    if (queue) {
        ret = pthread_mutex_init(&queue->mutex, NULL);
        if (ret == 0) {
            ret = pthread_cond_init(&queue->cond, NULL);
            if (ret == 0) {
                queue->head = NULL;
                queue->tail = &queue->head;
                queue->block = 1;
                return queue;
            }
            pthread_mutex_destroy(&queue->mutex);
        }
        free(queue);
    }
    return NULL;
}

创建线程池:

thrdpool_t *
thrdpool_create(int thrd_count)
{
	thrdpool_t *pool;
	pool = (thrdpool_t*)malloc(sizeof(*pool));
	if (pool) {
        task_queue_t *queue = __taskqueue_create();
        if (queue) {
            pool->task_queue = queue;
            atomic_init(&pool->quit, 0);
            if (__threads_create(pool, thrd_count) == 0)
                return pool;
            __taskqueue_destroy(queue);
        }
        free(pool);
    }
    return NULL;
}

5.2.2 发布任务

int
thrdpool_post(thrdpool_t *pool, handler_pt func, void *arg) {
	if (atomic_load(&pool->quit) == 1) 
        return -1;
    task_t *task = (task_t*) malloc(sizeof(task_t));
    if (!task) return -1;
    task->func = func;
    task->arg = arg;
    __add_task(pool->task_queue, task);
    return 0;
}

添加任务:

static inline void
add_task(task_queue_t queue, void *task)
{
	void **link = (void**)task; //指向新的task的起始地址(8个字节)
	*link = NULL;  // task->next = NULL
	
	spinlock_lock(&queue->lock);
	*queue->tail /* 等价于 queue->tail->next */ = link;
	queue->tail = link;
	spinlock_unlock(&queue->lock);
	pthread_cond_signal(&queue->cond);
}

5.2.3 获取任务

在5.2.1__threads_create中,pthread_create(&pool->threads[i], &attr, __thrdpool_worker, pool调用了__thrdpool_worker函数,__thrdpool_worker执行了__get_task函数。

static inline void * 
__get_task(task_queue_t *queue) {
    task_t *task;
    // 队列为空,阻塞消费者线程
    while ((task = __pop_task(queue)) == NULL) {
        pthread_mutex_lock(&queue->mutex);
        if (queue->block == 0) { //非阻塞,表明退出了
            pthread_mutex_unlock(&queue->mutex);
            return NULL;
        }
        // pthread_cond_wait流程:
        // 1. 先 unlock(&mtx)
        // 2. 在 cond 休眠
        // --- __add_task 时唤醒
        // 3. 在 cond 唤醒
        // 4. 加上 lock(&mtx);
        pthread_cond_wait(&queue->cond, &queue->mutex);
        pthread_mutex_unlock(&queue->mutex);
    }
    return task;
}
static inline void * 
__pop_task(task_queue_t *queue) {
    spinlock_lock(&queue->lock);
    if (queue->head == NULL) {
        spinlock_unlock(&queue->lock);
        return NULL;
    }
    task_t *task;
    task = queue->head;

    void **link = (void**)task;
    queue->head = *link; // *link为next指针

    if (queue->head == NULL) {
        queue->tail = &queue->head;
    }
    spinlock_unlock(&queue->lock);
    return task;
}

5.2.4 终止线程

void
thrdpool_terminate(thrdpool_t *pool)
{
	atomic_store(&pool->quit, 1);
	__nonblock(pool->task_queue);
}
static void
__nonblock(task_queue_t *queue) {
    pthread_mutex_lock(&queue->mutex);
    queue->block = 0;
    pthread_mutex_unlock(&queue->mutex);
    pthread_cond_broadcast(&queue->cond);
}

参考链接:
https://xxetb.xetslk.com/s/1QH6AQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值