C语言线程池的常见实现方式详解
1.基于C语言实现UDP服务器2.C++中线程同步与互斥的四种方式介绍及对比详解3.Jsoncpp的安装与使用方式4.C++ open()和read()函数使用详解5.深入理解C++ 空类大小6.C++17 Filesystem 实用教程7.C语言格式输出方式
8.C语言线程池的常见实现方式详解
9.opencv实现像素统计的示例代码10.C/C++ 创建Socket实现双工通信11.在Linux系统中下载`gcc-linaro-7.2.1-2017.11-x86_64_aarch64-linux-gnu`工具链12.linux安装lspci13.制作ubuntu22.04的根文件系统14.【ARM+Qt+OpenCV】基于ARM的双目图像采集系统15.通用C++ Makefile16.linux下安装cmake版本17.添加xxx.so到环境变量里18.在Linux系统下验证万兆网络(10Gbps)的性能和配置情况,可以通过多种方法来实现19.【Ryan】: linux下安装ftp20.【Ryan】: linux下挂在共享文件夹21.linux下配置ip为动态获取22.字符串值相加校验23.数据十六进制打印输出24.linux服务问题传文件连不上问题远程问题等25.提供一个纯C语言的图像压缩程序,但是要达到将6MB的图片压缩到100KB以内的要求,有损压缩肯定是必须的。同时,要在速度上有所提升,我可以为您提供一个基于多线程的图像压缩程序。26.cmake工程构建开发过程27.使用QT开发远程linux服务器过程28.linux系统测试磁盘IO速度 - fio使用在 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语言线程池的常见实现方式详解的文章就介绍到这了
本文来自博客园,作者:Ryan,转载请注明原文链接:https://www.cnblogs.com/Ryan9399/p/18675240
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!