C语言线程池的常见实现方式详解

2025-01-07 03:50

本文主要是介绍C语言线程池的常见实现方式详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧...

在 C 语言中,线程池通常通过 pthread 库来实现。以下是一个详细的说明,介绍了 C 语言线程池的常见实现方式,包括核心概念、实现步骤和具体的代码示例。

1. 线程池的基本结构

线程池的核心概念是有一个固定数量的线程等待执行任务。任务通常通过任务队列传递,线程从队列中取出任务并执行。线程池的主要目标是提高资源利用率,避免频繁地创建和销毁线程。

线程池的主要组件:

  • 任务结构:保存任务信息,比如任务的函数指针和参数。
  • 任务队列:用于存放待处理的任务。当所有工作线程都在忙时,新提交的任务会被放到队列中,直到线程空闲出来。
  • 线程池控制:管理线程池的线程,调度任务的分发,维护任务队列。

2. 线程池的实现步骤

以下是实现一个简单线程池的基本步骤:

初始化线程池

创建一定数量的线程,并使它们处于等待状态。

创建一个任务队列,用来存储待执行的任务。

任务提交

用户提交任务到线程池,线程池会把任务放入任务队列中,等待工作线程去执行。

工作线程

工作线程从任务队列中取出任务并执行。

如果没有任务,线程会阻塞,直到有任务提交到任务队列。

关闭线程池

关闭线程池时,需要确保所有任务完成后再销毁线程池,并且释放所有资源。

3. 线程池的核心数据结构

任务结构: 每个任务通常包括任务的执行函数和任务的参数。

typedef struct {
    void (*routine)(void *arg);  // 任务执行的函数
    void *arg;                   // 传递给任务函数的参数
} task_t;

线程池结构: 线程池需要包含任务队列、线程数组、线程数量、锁以及条件变量等。

typedef struct {
    pthread_t *threads;           // 工作线程数组
    task_t *task_queue;           // 任务队列
    int queue_size;               // 队列大小
    int head, tail;               // 队列头尾索引
    int thread_count;             // 线程池中的线程数
    pthread_mutex_t lock;        // 锁,保护任务队列
    pthread_cond_t cond;         // 条件变量,唤醒工作线程
    int shutdown;                 // 是否关闭线程池
} thread_pool_t;

4. 线程池的详细实现

下面是一个完整的线程池实现,包括初始化、任务提交、任务执行和销毁。

4.1 初始化线程池

首先需要创建线程池,并初始化必要的数据结构。

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
typedef struct {
    void (*routine)(void *arg);  // 任务执行的函数
    void *arphpg;                   // 传递给任务函数的参数
} task_t;
typedef struct {
    pthread_t *threads;           // 工作线程数组
    task_t *task_queue;           // 任务队列
    int queue_size;               // 队列大小
    int head, tail;    javascript     China编程      // 队列头尾索引
    int thread_count;             // 线程池中的线程数
    pthread_mutex_t lock;        // 锁,保护任务队列
    pthread_cond_t cond;         // 条件变量,唤醒工作线程
    int shutdown;                 // 是否php关闭线程池
} thread_pool_t;
void *worker(void *arg) {
    thread_pool_t *pool = (thread_pool_t *)arg;
    while (1) {
        pthread_mutex_lock(&pool->lock);
        while (pool->head == pool->tail && !pool->shutdown) {
            pthread_cond_wait(&pool->cond, &pool->lock);  // 等待任务
        }
        // 检查是否关闭线程池
        if (pool->shutdown) {
            pthread_mutex_unlock(&pool->lock);
            break;
        }
        task_t task = pool->task_queue[pool->head];  // 获取任务
        pool->head = (pool->head + 1) % pool->queue_size;  // 队列中移除任务
        pthread_mutex_unlock(&pool->lock);
        task.routine(task.arg);  // 执行任务
    }
    pthread_exit(NULL);
}
void thread_pool_init(thread_pool_t *pool, int thread_count, int queue_size) {
    pool->threads = (pthread_t *)malloc(thread_count * sizeof(pthread_t));
    pool->task_queue = (task_t *)malloc(queue_size * sizeof(task_t));
    pool->queue_size = queue_size;
    pool->head = pool->tail = 0;
    pool->thread_count = thread_count;
    pool->shutdown = 0;
    pthread_mutex_init(&pool->lock, NULL);
    pthread_cond_init(&pool->cond, NULL);
    // 创建线程
    for (int i = 0; i < thread_count; i++) {
        pthread_create(&pool->threads[i], NULL, worker, pool);
    }
}

4.2 提交任务

当用户需要执行某个任务时,任务会被加入任务队列,等待线程执行。

void thread_pool_add_task(thread_pool_t *pool, void (*routine)(void *), void *arg) {
    pthread_mutex_lock(&pool->lock);
    // 检查任务队列是否满
    if ((pool->tail + 1) % pool->queue_size != pool->head) {
        pool->task_queue[pool->tail].routine = routine;
        pool->task_queue[pool->tail].arg = arg;
        pool->tail = (pool->tail + 1) % pool->queue_size;  // 更新队列尾部
        pthread_cond_signal(&pool->cond);  // 唤醒一个工作线程
    }
    pthread_mutex_unlock(&pool->lock);
}

4.3 关闭线程池

关闭线程池时,需要等待所有线程处理完任务后才能销毁线程池。可以通过设置 shutdown 标志来通知线程池停止。

void thread_pool_destroy(thread_pool_t *pool) {
    pthread_mutex_lock(&pool->lock);
China编程    pool->shutdown = 1;
    pthread_cond_broadcast(&pool->cond);  // 唤醒所有线程,确保线程能够退出
    pthread_mutex_unlock(&pool->lock);
    // 等待所有线程退出
    for (int i = 0; i < pool->thread_count; i++) {
        pthread_join(pool->threads[i], NULL);
    }
    free(pool->threads);
    free(pool->task_queue);
    pthread_mutex_destroy(&pool->lock);
    pthread_cond_destroy(&pool->cond);
}

4.4 示例任务函数

用户可以定义自己的任务函数,传递参数,并在任务函数中执行实际的工作。

void print_hello(void *arg) {
    printf("Hello, %s!\n", (char *)arg);
}
int main() {
    thread_pool_t pool;
    thread_pool_init(&pool, 4, 10);  // 创建一个线程池,包含4个线程和10个任务队列
    for (int i = 0; i < 5; i++) {
        char *name = malloc(10);
        sprintf(name, "Task %d", i + 1);
        thread_pool_add_task(&pool, print_hello, name);
    }
    sleep(1);  // 等待任务执行
    thread_pool_destroy(&pool);  // 销毁线程池
    return 0;
}

5. 线程池的调优和优化

在实际应用中,线程池的性能可以通过以下几个方面进行调优和优化:

  • 最大线程数和最小线程数:为了避免线程过多导致的资源竞争,可以设置最小线程数和最大线程数。
  • 任务队列长度:任务队列的长度要适中。过长的队列可能导致任务过度堆积,过短的队列则可能导致线程池无法充分利用资源。
  • 动态线程调整:根据系统负载动态增加或减少线程数量,能够提高系统的效率和响应速度。
  • 任务超时机制:为了防止某些任务长时间占用线程,线程池可以设置任务的超时机制,当任务超时后放弃执行或重新调度。

总结

本文介绍了如何使用 C 语言实现一个基本的线程池。线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤。通过这种方式,可以在多任务、高并发的场景中有效地管理线程,减少线程创建和销毁的开销,提高系统的效率。

到此这篇关于C语言线程池的常见实现方式详解的文章就介绍到这了,更多相关C语言线程池内容请搜索编程China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于C语言线程池的常见实现方式详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1152953

相关文章

Springboot使用RabbitMQ实现关闭超时订单(示例详解)

《Springboot使用RabbitMQ实现关闭超时订单(示例详解)》介绍了如何在SpringBoot项目中使用RabbitMQ实现订单的延时处理和超时关闭,通过配置RabbitMQ的交换机、队列和... 目录1.maven中引入rabbitmq的依赖:2.application.yml中进行rabbit

Spring使用@Retryable实现自动重试机制

《Spring使用@Retryable实现自动重试机制》在微服务架构中,服务之间的调用可能会因为一些暂时性的错误而失败,例如网络波动、数据库连接超时或第三方服务不可用等,在本文中,我们将介绍如何在Sp... 目录引言1. 什么是 @Retryable?2. 如何在 Spring 中使用 @Retryable

Python如何实现 HTTP echo 服务器

《Python如何实现HTTPecho服务器》本文介绍了如何使用Python实现一个简单的HTTPecho服务器,该服务器支持GET和POST请求,并返回JSON格式的响应,GET请求返回请求路... 一个用来做测试的简单的 HTTP echo 服务器。from http.server import HT

C#中字符串分割的多种方式

《C#中字符串分割的多种方式》在C#编程语言中,字符串处理是日常开发中不可或缺的一部分,字符串分割是处理文本数据时常用的操作,它允许我们将一个长字符串分解成多个子字符串,本文给大家介绍了C#中字符串分... 目录1. 使用 string.Split2. 使用正则表达式 (Regex.Split)3. 使用

Python绘制土地利用和土地覆盖类型图示例详解

《Python绘制土地利用和土地覆盖类型图示例详解》本文介绍了如何使用Python绘制土地利用和土地覆盖类型图,并提供了详细的代码示例,通过安装所需的库,准备地理数据,使用geopandas和matp... 目录一、所需库的安装二、数据准备三、绘制土地利用和土地覆盖类型图四、代码解释五、其他可视化形式1.

opencv实现像素统计的示例代码

《opencv实现像素统计的示例代码》本文介绍了OpenCV中统计图像像素信息的常用方法和函数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 统计像素值的基本信息2. 统计像素值的直方图3. 统计像素值的总和4. 统计非零像素的数量

SpringBoot使用Apache POI库读取Excel文件的操作详解

《SpringBoot使用ApachePOI库读取Excel文件的操作详解》在日常开发中,我们经常需要处理Excel文件中的数据,无论是从数据库导入数据、处理数据报表,还是批量生成数据,都可能会遇到... 目录项目背景依赖导入读取Excel模板的实现代码实现代码解析ExcelDemoInfoDTO 数据传输

Python使用asyncio实现异步操作的示例

《Python使用asyncio实现异步操作的示例》本文主要介绍了Python使用asyncio实现异步操作的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋... 目录1. 基础概念2. 实现异步 I/O 的步骤2.1 定义异步函数2.2 使用 await 等待异

如何用Java结合经纬度位置计算目标点的日出日落时间详解

《如何用Java结合经纬度位置计算目标点的日出日落时间详解》这篇文章主详细讲解了如何基于目标点的经纬度计算日出日落时间,提供了在线API和Java库两种计算方法,并通过实际案例展示了其应用,需要的朋友... 目录前言一、应用示例1、天安门升旗时间2、湖南省日出日落信息二、Java日出日落计算1、在线API2

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象