在 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 *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;
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);
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语言线程池的常见实现方式详解的更多相关文章

  1. 线程池的使用(ThreadPoolExecutor详解)

    为什么要使用线程池? 线程是一个操作系统概念.操作系统负责这个线程的创建.挂起.运行.阻塞和终结操作.而操作系统创建线程.切换线程状态.终结线程都要进行CPU调度——这是一个耗费时间和系统资源的事情. ...

  2. 线程池ThreadPoolExecutor、Executors参数详解与源代码分析

    欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. ThreadPoolExecutor数据成员 Private final Atom ...

  3. Java线程同步的四种方式详解(建议收藏)

    ​ Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式@mikechen 目录 什么是线程同步 线程同步的几种方式 1.使用sync ...

  4. C语言 文件操作6--文件打开方式详解

    fopen文件打开模式 r代表read的简写,+代表可读可写,w代表write,b代表bit二进制位,t代表text r 打开只读文件,该文件必须存在r+ 打开可读可写的文件,该文件必须存在(这里的写 ...

  5. Android 应用开发 之通过AsyncTask与ThreadPool(线程池)两种方式异步加载大量数据的分析与对比--转载

     在加载大量数据的时候,经常会用到异步加载,所谓异步加载,就是把耗时的工作放到子线程里执行,当数据加载完毕的时候再到主线程进行UI刷新.在数据量非常大的情况下,我们通常会使用两种技术来进行异步加载,一 ...

  6. 常见 jar包详解

        常见 jar包详解 jar包 用途 axis.jar SOAP引擎包 commons-discovery-0.2.jar 用来发现.查找和实现可插入式接口,提供一些一般类实例化.单件的生命周期 ...

  7. JavaEE实战——XML文档DOM、SAX、STAX解析方式详解

    原 JavaEE实战--XML文档DOM.SAX.STAX解析方式详解 2016年06月22日 23:10:35 李春春_ 阅读数:3445 标签: DOMSAXSTAXJAXPXML Pull 更多 ...

  8. JSON取值(key是中文或者数字)方式详解

    JSON取值(key是中文或者数字)方式详解 先准备一个json对象用于演示 var json = {'name':'zhangsan', '年龄':23, 404:'你可能迷路了'}; 使用JS中w ...

  9. C语言对文件的操作函数用法详解2

    fopen(打开文件) 相关函数 open,fclose 表头文件 #include<stdio.h> 定义函数 FILE * fopen(const char * path,const  ...

  10. C语言对文件的操作函数用法详解1

    在ANSIC中,对文件的操作分为两种方式,即: 流式文件操作 I/O文件操作 一.流式文件操作 这种方式的文件操作有一个重要的结构FILE,FILE在stdio.h中定义如下: typedef str ...

随机推荐

  1. 从 $PGDATA 到文件组:深入解析 PostgreSQL 与 SQL Server 的存储策略

    从 $PGDATA 到文件组:深入解析 PostgreSQL 与 SQL Server 的存储策略 在数据库领域,数据存储和管理的效率与可靠性是决定系统性能.可扩展性和易于管理的关键因素.Postgr ...

  2. Qt编写可视化大屏电子看板系统20-横向分组图

    一.前言 横向分组图是柱状分组图的横向展示,有了横向柱状图加上分组图的绘制经验,这个做起来就比较简单了,横向的设置规则按照横向柱状图来,分组的规则按照柱状分组图的算法来,在横向的柱子中要绘制对应的值, ...

  3. 飞书lark机器人 自动化发版

    飞书lark机器人 自动化发版 #1 介绍 开发飞书机器人接收消息并调用构建接口, 实现自动化发版 发送指令 -> 机器人接收指令 -> 调用jenkins-job远程构建与部署 jenk ...

  4. python基础应用

    pip的使用 升级pip python3 -m pip install --upgrade pip 镜像源设置 查看镜像源 pip config list 指定镜像源更新依赖 pip3 install ...

  5. Linux 终端

    在 Linux 系统中,终端设备通常分为主设备和从设备.这是一种特殊的设备对的概念,其中: 主设备: 主设备也称为 "master device". 它是终端设备的控制端,用于与用 ...

  6. centos 安装python3后yum报错

    问题 centos 安装python3后,并且把/usr/bin/python 软链接到python3后,yum命令报错 原因: yum命令依赖于python2,导致报错 解决方法: 修改 /usr/ ...

  7. linux 安装 Ollama 框架

    概述 Ollama 是一款旨在简化大语言模型(LLM)本地部署的工具,支持 Windows.Linux 和 MacOS 系统.它提供了一个用户友好的环境,让开发者可以轻松地运行和调优如 Qwen.Ll ...

  8. Solution Set -「LOCAL」冲刺省选 Round XXXI

    \(\mathscr{Summary}\)   前期节奏太懒散,后面发现 C 题是水题都没时间写,提起精神来啊!   A 题卡得比较久,对线性基的理解不够深刻,思来想去半天才把转移系数调对.B 题也卡 ...

  9. 深入理解ReentrantLock的实现原理

    文章目录ReentrantLock简介AQS回顾ReentrantLock原理ReentrantLock结构非公平锁的实现原理lock方法获取锁tryRelease锁的释放公平锁的实现原理lock方法 ...

  10. biancheng-Spring Cloud Alibaba Sentinel

    http://c.biancheng.net/springcloud/sentinel.html Sentinel 是由阿里巴巴中间件团队开发的开源项目,是一种面向分布式微服务架构的轻量级高可用流量控 ...