c/c++ 线程池原理简单代码实现

本文深入探讨线程池的工作原理,分析其如何通过预创建线程减少资源开销,并详细解析线程池的四大部分:线程队列、任务队列、信号量和互斥量。同时,提供了一个具体的线程池代码实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 线程池原理

       在传统服务器结构中,常用一个总的监听线程监听新用户连接,当有一个新用户进入时,服务器就开启一个新的线程,用于处理这个用户的数据收发,这个线程只服务于这个用户,当用户与服务器端连接关闭以后,服务器将销毁这个线程
       然而频繁地开辟与销毁线程会极大地占用系统资源,线程池的基本思想是提前创建好一些线程,有新任务到来时,则在线程队列中找到一个空闲线程来处理,处理完后线程不退出重回空闲状态继续等待新任务。如果没有空闲线程,则将任务存放到队列, 等待线程池内有线程空闲以后再从任务队列取出任务进行处理。这样就避免了线程创建和销毁的系统资源开销
       线程池应用场景:一个应用要频繁的创建和销毁线程,但任务执行的时间又非常短,这样线程创建和销毁的系统资源开销就不容忽视,这时就可以使用线程池了

2. 实现分析

线程池包含四个部分:

       i. 线程队列,存储创建的多个线程

       ii. 任务队列,存储需要处理的任务,使用 list 链表实现,当有新任务到来时,将节点插入到 list 尾部,当空闲线程读取任务进行处理时,则读取 list 头部节点

       iii. 信号量 sem,指示任务队列中的任务个数,当有新任务加入时,执行 sem_post 操作+1,空闲线程通过 sem_wait 等待任务队列变成非空状态

       iv. 互斥量 mutex, 用于执行添加/移除 list 节点时,进行多线程保护

a. 创建并开始多个线程,线程执行函数是个 while 循环,循环等待任务队列不为空,一旦不为空,则读取 list 头节点,获取任务执行方法和参数,然后执行任务,完成后释放头结点空间

b. 添加新任务到队列,申请新任务节点空间,用于存储新任务执行方法和参数,然后将新任务节点插入到 list 的尾部

3. 代码实现

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

#define     MAXNUM_THREAD  3
#define     MAXNUM_JOB     10

typedef void* (*Func)(void *arg);

struct job {
    Func func; 
    void *arg; 
    struct job *next;
};

struct pool { 
    pthread_t threads[MAXNUM_THREAD];
    struct job *head;
    pthread_mutex_t mutex; // used for add/remove list node action
    sem_t sem; // indicate threads can get one job node from list
};

void* threadFunc(void* arg)
{
    Func jobFunc;
    void *jobArg;
    struct job *head;
    struct pool *pl = (struct pool*)arg;

    while (1)
    {        
        sem_wait(&(pl->sem)); // wait new job node added to list
        pthread_mutex_lock(&(pl->mutex)); // wait other threads complete list action
        head = pl->head;
        pl->head = pl->head->next; // unlink head node from list, free after func done
        pthread_mutex_unlock(&(pl->mutex));

        jobFunc = head->func;
        jobArg = head->arg;
        jobFunc(jobArg);
        free(head); // free unlinked head node
    }
}

void poolInit(struct pool *pl)
{
    pl->head = NULL;
    pthread_mutex_init(&(pl->mutex), NULL);
    sem_init(&(pl->sem), 0, 0);

    for (int i=0; i<MAXNUM_THREAD; i++)
    {
        pthread_create(&(pl->threads[i]), NULL, threadFunc, (void *)pl);
        printf("thread created: %u\n", (unsigned int)(pl->threads[i]));
    }
}

void addJob(struct pool *pl, Func func, void *arg)
{
    struct job* jobNode = (struct job*)malloc(sizeof(struct job));
    jobNode->func = func;
    jobNode->arg = arg;
    jobNode->next = NULL;

    pthread_mutex_lock(&(pl->mutex));
    if (NULL == pl->head)     // add job node to list tail
    {
        pl->head = jobNode;
    }
    else
    {
        struct job *head = pl->head;
        while (head->next)
        {
            head = head->next;
        } 
        head->next = jobNode; 
    }
    pthread_mutex_unlock(&(pl->mutex));
    sem_post(&(pl->sem)); // threads can get job node from list
}
 
void* jobFunc(void* arg)
{
    int index = *((int*)arg);
    printf("job: %d started by thread: %u\n", index, (unsigned int)pthread_self());
    sleep(rand()%5);
    printf("job: %d ended\n", index);
}

int main()
{
    int i = 0;
    int jobIds[MAXNUM_JOB] = {0};
    struct pool pl;

    poolInit(&pl);
    for (i=0; i<MAXNUM_JOB; i++)
    {
        jobIds[i] = i;
        printf("job: %d added\n", i);
        addJob(&pl, jobFunc, (void*)&jobIds[i]); // add job node to list
        sleep(rand()%2);
    }

    for (i=0; i<MAXNUM_THREAD; i++)
    {
        pthread_join(pl.threads[i], NULL);
    }
    pthread_mutex_destroy(&(pl.mutex));
    sem_destroy(&(pl.sem));

    return 0;
}

运行结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值