【UNIX环境高级编程】c语言实现线程池(基础版)

目录

线程池代码

头文件 threadpool.h 

源文件 threadpool.c

头文件 main.c

线程池相关原理

为什么要用线程池?什么是线程池?

线程池的组成

线程池参数设置 


为了方便一些初学同学能更快的实现一个线程池,我这里先给大家一个简单的线程池的代码。本文是一个线程池的基础实现,后续会发进阶版的线程池(添加管理者线程、线程动态扩容缩容等机制)。

本文后面进行线程池基础相关内容的介绍。

线程池代码

头文件 threadpool.h 

#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/*任务结构体*/
typedef struct 
{
    void (*function)(void *);
    void *arg;
} Task;

/*线程池结构体*/
typedef struct 
{
    pthread_t *threads;
    Task *taskQueue;
    int threadCount;
    int queueSize;
    int head;
    int tail;
    int count;
    int shutdown;
    pthread_mutex_t lock;
    pthread_cond_t notify;
} ThreadPool; 

/*任务函数*/ 
static void exampleTask(void *arg) 
{
    int *num = (int *)arg;
    printf("正在处理数字: %d (线程 ID: %lu)\n", *num, pthread_self());
    free(num);  /*释放动态分配的内存*/
}

/* 初始化线程池 */ 
ThreadPool *threadPoolInit(int threadCount, int queueSize);
/* 添加任务到线程池 */ 
int threadPoolAdd(ThreadPool *pool, void (*function)(void *), void *arg);
/* 销毁线程池 */ 
int threadPoolDestroy(ThreadPool *pool);
/* 线程工作函数 */ 
void *threadWork(void *arg);

#endif // THREADPOOL_H

源文件 threadpool.c

#include "threadpool.h"

/* 线程工作函数 */ 
void *threadWork(void *arg) 
{
    /* 拿到线程池指针 */ 
    ThreadPool *pool = (ThreadPool *)arg;
    
    while (1) 
    {
        pthread_mutex_lock(&(pool->lock));
        
        /* 线程池未关闭,且线程池为空,等待任务 */ 
        while (pool->count == 0 && !pool->shutdown) 
        {
            pthread_cond_wait(&(pool->notify), &(pool->lock));
        }
        
        /* 线程池关闭,解锁、关闭线程 */ 
        if (pool->shutdown) 
        {
            pthread_mutex_unlock(&(pool->lock));
            pthread_exit(NULL);
        }
        
        /* 取出任务 */ 
        Task task;
        task.function = pool->taskQueue[pool->head].function;
        task.arg = pool->taskQueue[pool->head].arg;
        
        /* 更新队列 */ 
        pool->head = (pool->head + 1) % pool->queueSize;
        pool->count--;
        
        pthread_mutex_unlock(&(pool->lock));
        
        /* 执行任务 */ 
        (*(task.function))(task.arg);
    }
    
    return NULL;
}

/* 初始化线程池 */ 
ThreadPool *threadPoolInit(int threadCount, int queueSize) 
{
    ThreadPool *pool = (ThreadPool *)malloc(sizeof(ThreadPool));
    if (pool == NULL) 
    {
        return NULL;
    }
    
    /* 初始化 */ 
    pool->threadCount = threadCount;
    pool->queueSize = queueSize;
    pool->head = 0;
    pool->tail = 0;
    pool->count = 0;
    pool->shutdown = 0;
    pthread_mutex_init(&(pool->lock), NULL);
    pthread_cond_init(&(pool->notify), NULL);
    pool->threads = (pthread_t *)malloc(sizeof(pthread_t) * threadCount);
    pool->taskQueue = (Task *)malloc(sizeof(Task) * queueSize);
    
    /* 创建工作线程 */ 
    for (int i = 0; i < threadCount; i++) 
    {
        pthread_create(&(pool->threads[i]), NULL, threadWork, (void *)pool);
    }
    
    return pool;
}

/* 添加任务到线程池 */ 
int threadPoolAdd(ThreadPool *pool, void (*function)(void *), void *arg) {
    int err = 0;
    
    if (pool == NULL || function == NULL) 
    {
        return -1;
    }
    
    pthread_mutex_lock(&(pool->lock));
    
    /* 检查队列是否已满 */ 
    if (pool->count == pool->queueSize) 
    {
        pthread_mutex_unlock(&(pool->lock));
        return -1;
    }
    
    /* 检查是否已关闭 */ 
    if (pool->shutdown) 
    {
        pthread_mutex_unlock(&(pool->lock));
        return -2;
    }
    
    /* 添加任务到队列 */ 
    pool->taskQueue[pool->tail].function = function;
    pool->taskQueue[pool->tail].arg = arg;
    pool->tail = (pool->tail + 1) % pool->queueSize;
    pool->count++;
    
    /* 通知一个等待线程 */ 
    pthread_cond_signal(&(pool->notify));
    
    pthread_mutex_unlock(&(pool->lock));
    return err;
}

/* 销毁线程池 */ 
int threadPoolDestroy(ThreadPool *pool) 
{
    /* 设置关闭标志 */
    pool->shutdown = 1;

    if (pool == NULL) 
    {
        return -1;
    }
    
    pthread_mutex_lock(&(pool->lock));
    
    /* 已关闭则直接返回 */ 
    if (pool->shutdown) 
    {
        pthread_mutex_unlock(&(pool->lock));
        return -2;
    }
    
    /* 唤醒所有线程 */ 
    pthread_cond_broadcast(&(pool->notify));
    pthread_mutex_unlock(&(pool->lock));
    
    /* 等待所有线程退出 */ 
    for (int i = 0; i < pool->threadCount; i++) 
    {
        pthread_join(pool->threads[i], NULL);
    }
    
    /* 释放资源 */ 
    free(pool->threads);
    free(pool->taskQueue);
    
    /* 销毁锁和条件变量 */ 
    pthread_mutex_destroy(&(pool->lock));
    pthread_cond_destroy(&(pool->notify));
    
    free(pool);
    return 0;
}

头文件 main.c

#include "threadpool.h"

int main() 
{
    /*创建线程池:4个线程,任务队列大小为10*/
    ThreadPool *pool = threadPoolInit(4, 10);
    
    /*添加20个任务*/
    for (int i = 0; i < 20; i++) 
    {
        int *arg = malloc(sizeof(int));
        *arg = i;
        threadPoolAdd(pool, exampleTask, arg);
    }
    
    /*等待所有任务完成*/
    sleep(2);
    
    /*销毁线程池*/
    threadPoolDestroy(pool);
    
    return 0;
}

线程池相关原理

为什么要用线程池?什么是线程池?

我们正常情况下并发的去处理任务时,通常是每来一个任务我们去创建一个线程为这个任务服务,任务结束的时候又去销毁线程。当然,这样频繁地创建和销毁线程的开销是非常大的,而且这样会导致线程非常分散,不方便我们去管理,也可能会造成线程数量太多,导致系统过载。所以我们就引入了线程池来解决这个问题。

线程池就是在任务开始前,我们提前创建一些线程,当任务来了,我们直接从线程池中取出一个空闲的线程去执行任务。任务执行完毕,将线程再放回线程池中,等待下一个任务到来。这样我们就可以避免频繁的去创建和销毁线程,提高了线程的一个复用性。

线程池的组成

从功能的角度,线程池主要需要三个核心的模块。 

  • 线程池管理者(PoolController):负责创建、管理和控制线程池。一个线程池不是凭空出现的,我们会先有一个管理者线程去进行线程池的初始化工作和销毁工作,并且在后续根据工作的需求去动态的调整线程池(如动态扩容等)。
  • 任务队列(TaskQueue):缓存待执行的任务。由于线程池是我们预先就创建好的,我们还要体现出线程的一个复用的功能。我们需要将任务平均的分配到每个线程上,我们需要在中间做一个缓存的机制,这个就是任务队列。线程池中的空闲线程不断地去任务队列中取任务进行执行,我们就可以将压力平均分到所有线程上。
  • 工作线程(ThreadWorkers):实际执行任务的线程。线程池中维护这么一组线程,用于复用的去执行任务。

线程池参数设置 

线程池初始化的时候线程数应该设置成多少呢?是越多越好吗?这里是有一个合适的标准的。

任务类型推荐初始值原因
CPU密集型corePoolSize = CPU核数避免过多线程竞争CPU,减少上下文切换开销(如视频编码、数值计算)。
I/O密集型corePoolSize = 2*CPU核数线程可能因I/O阻塞(如网络请求、数据库操作),适当增加线程利用率更高。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值