当前位置: 首页 > news >正文

【线程系列之五】线程池介绍C语言

一、基本概念

1.1 概念

线程池(Thread Pool)是一种基于池化技术管理线程的机制,旨在减少线程创建和销毁的开销,提高系统资源的利用率,以及更好地控制系统中同时运行的线程数量。线程池通过预先创建一定数量的线程,并将这些线程放入一个“池”中,当有新任务到来时,不是立即创建新线程来执行,而是从线程池中取出一个空闲的线程来执行该任务。如果所有线程都在忙碌,则新任务会等待直到有线程变得空闲。

在C语言中,由于标准库(如C89/C99/C11等)不支持线程或线程池,因此通常需要使用第三方库如POSIX线程(pthread)来实现线程池。

1.2 应用场景【C语言】

C语言中的线程池应用场景与在其他编程语言中类似,主要包括以下几类:

  • 高并发服务器: 在网络服务器中处理大量客户端请求。每个请求可以分配给一个线程池中的线程进行处理,以提高响应速度和吞吐量。

  • 数据处理和计算密集型任务: 当需要处理大量数据或执行复杂的计算时,可以将任务分配到线程池中并行执行,以缩短总体执行时间。

  • 资源密集型任务: 对于需要频繁访问共享资源(如数据库、文件系统等)的任务,使用线程池可以减少线程创建和销毁的开销,并可能通过更高效的资源管理来提高性能。

  • 异步操作: 在需要执行非阻塞操作(如异步I/O、异步网络请求等)时,线程池可以用来处理这些异步操作的结果或回调。

  • 定时任务和周期性任务: C语言本身不直接支持定时器和周期性任务,但你可以使用线程池中的线程来模拟这些功能,通过轮询或睡眠机制来检查时间并执行相应的任务。

1.3 实现线程池步骤
  • 定义线程池结构: 包括线程数量、任务队列、互斥锁、条件变量等;

  • 实现任务队列: 用于存储待执行的任务;

  • 编写线程工作函数: 该函数将被多个线程同时执行,并从任务队列中取出任务进行处理;

  • 初始化和管理线程池: 包括创建线程、启动线程、销毁线程等操作;

  • 添加任务到线程池: 提供接口将新任务添加到任务队列中,并通知等待的线程。

由于C语言相对底层,因此实现这些功能需要更多的手动编码和对系统资源的管理。此外,你还需要考虑线程安全和性能优化等问题,第三方库可在C++、python中查找,避免从头实现线程池。

下文将具体说明C语言实现线程池的代码。

二、实现

2.1 定义线程池结构

首先,定义一个包含线程池所需所有信息的结构体,如线程数组、任务队列、互斥锁、条件变量等。

#include <pthread.h>  
#include <stdbool.h>  
#include <stdlib.h> #define MAX_THREADS 4 // 任务队列节点  
typedef struct task {  void (*function)(void*);  void* arg;  struct task* next;  
} task_t;  // 线程池结构体  
typedef struct {  pthread_t threads[MAX_THREADS]; // 线程数组  pthread_mutex_t queue_mutex;    // 保护任务队列的互斥锁  pthread_cond_t queue_cond;      // 任务队列的条件变量  bool stop;                      // 线程池停止标志  task_t* head;                   // 任务队列头指针  task_t* tail;                   // 任务队列尾指针  int thread_count;               // 当前活跃线程数  int active_threads;             // 最大活跃线程数(可配置)  
} threadpool_t;
2.2 初始化线程池

实现一个函数来初始化线程池,包括创建线程、初始化同步等。

void* worker(void* arg) {  threadpool_t* pool = (threadpool_t*)arg;  while (true) {  pthread_mutex_lock(&pool->queue_mutex);  // 如果线程池已停止且没有任务,则退出循环  if (pool->stop && pool->head == NULL) {  pthread_mutex_unlock(&pool->queue_mutex);  break;  }  // 等待任务或线程池停止信号  while (!pool->stop && pool->head == NULL && pool->thread_count >= pool->active_threads) {  pthread_cond_wait(&pool->queue_cond, &pool->queue_mutex);  }  // 取出任务(如果线程池未停止且队列不为空)  task_t* task = NULL;  if (!pool->stop && pool->head != NULL) {  task = pool->head;  pool->head = task->next;  if (pool->head == NULL) {  pool->tail = NULL;  }  pool->thread_count--;  }  pthread_mutex_unlock(&pool->queue_mutex);  // 执行任务(如果任务不为空)  if (task != NULL) {  (*(task->function))(task->arg);  free(task); // 释放任务节点内存(如果任务节点是动态分配的)  }  }  return NULL;  
}  void threadpool_init(threadpool_t* pool, int active_threads) {  pool->stop = false;  pool->head = pool->tail = NULL;  pool->thread_count = 0;  pool->active_threads = active_threads;  pthread_mutex_init(&pool->queue_mutex, NULL);  pthread_cond_init(&pool->queue_cond, NULL);  for (int i = 0; i < active_threads; i++) {  pthread_create(&pool->threads[i], NULL, worker, pool);  }  
}
2.3 添加任务到线程池

实现一个函数来向线程池的任务队列中添加任务,并唤醒一个等待的线程(如果有的话)。

void threadpool_add_task(threadpool_t* pool, void (*function)(void*), void* arg) {  task_t* new_task = (task_t*)malloc(sizeof(task_t));  new_task->function = function;  new_task->arg = arg;  new_task->next = NULL;  pthread_mutex_lock(&pool->queue_mutex);  if (pool->tail == NULL) {  pool->head = pool->tail = new_task;  } else {  pool->tail->next = new_task;  pool->tail = new_task;  }  pthread_cond_signal(&pool->queue_cond);  pthread_mutex_unlock(&pool->queue_mutex);  
}
2.4 销毁线程池

实现一个函数来停止所有线程并销毁线程池。

void threadpool_destroy(threadpool_t* pool) {  pool->stop = true;  pthread_cond_broadcast(&pool->queue_cond);  for (int i = 0; i < MAX_THREADS; i++) {  pthread_join(pool->threads[i], NULL);  }  pthread_mutex_destroy(&pool->queue_mutex);  pthread_cond_destroy(&pool->queue_cond);  
}

三、完整简单示例

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>#define MAX_THREADS 5typedef struct task {void (*function)(void*);void* arg;struct task* next;
} task_t;typedef struct {pthread_t threads[MAX_THREADS];pthread_mutex_t queue_mutex;pthread_cond_t queue_cond;bool stop;task_t* head;task_t* tail;int thread_count;int active_threads;
} threadpool_t;void* worker(void* arg) {threadpool_t* pool = (threadpool_t*)arg;while (true) {pthread_mutex_lock(&pool->queue_mutex);while (pool->head == NULL && !pool->stop) {pthread_cond_wait(&pool->queue_cond, &pool->queue_mutex);}if (pool->stop && pool->head == NULL) {pthread_mutex_unlock(&pool->queue_mutex);break;}task_t* task = pool->head;if (task != NULL) {pool->head = task->next;if (pool->head == NULL) {pool->tail = NULL;}}pthread_mutex_unlock(&pool->queue_mutex);if (task != NULL) {(*(task->function))(task->arg);free(task); // 释放任务节点内存}sleep(1);}return NULL;
}void threadpool_init(threadpool_t* pool, int active_threads) {pool->stop = false;pool->head = pool->tail = NULL;pool->thread_count = 0;pool->active_threads = active_threads;pthread_mutex_init(&pool->queue_mutex, NULL);pthread_cond_init(&pool->queue_cond, NULL);for (int i = 0; i < active_threads; ++i) {pthread_create(&pool->threads[i], NULL, worker, pool);}
}void threadpool_add_task(threadpool_t* pool, void (*function)(void*), void* arg) {task_t* new_task = (task_t*)malloc(sizeof(task_t));new_task->function = function;new_task->arg = arg;new_task->next = NULL;pthread_mutex_lock(&pool->queue_mutex);if (pool->tail == NULL) {pool->head = pool->tail = new_task;} else {pool->tail->next = new_task;pool->tail = new_task;}pthread_cond_signal(&pool->queue_cond);pthread_mutex_unlock(&pool->queue_mutex);
}void threadpool_destroy(threadpool_t* pool) {pool->stop = true;pthread_mutex_lock(&pool->queue_mutex);pthread_cond_broadcast(&pool->queue_cond);pthread_mutex_unlock(&pool->queue_mutex);for (int i = 0; i < pool->active_threads; ++i) {pthread_join(pool->threads[i], NULL);printf("%d\r\n", i);}pthread_mutex_destroy(&pool->queue_mutex);pthread_cond_destroy(&pool->queue_cond);
}// 示例任务函数
void example_task(void* arg) {int task_id = *(int*)arg;printf("Task %d is executing\n", task_id);free(arg); // 释放参数内存sleep(1);  // 模拟任务执行时间
}int main() {threadpool_t pool;threadpool_init(&pool, 2); // 初始化线程池,最大活跃线程数为2// 添加示例任务for (int i = 0; i < MAX_THREADS; ++i) {int* arg = (int*)malloc(sizeof(int));*arg = i;threadpool_add_task(&pool, example_task, arg);}// 等待一段时间,观察任务执行情况sleep(5);// 销毁线程池threadpool_destroy(&pool);return 0;
}

运行结果为:

在这里插入图片描述

这段代码演示了一个简单的线程池的使用方式。在主函数中,我们初始化了一个线程池(最大活跃线程数为2),然后添加了5个示例任务(每个任务执行时间模拟为1秒)。在任务执行完毕后,通过调用 threadpool_destroy 函数来销毁线程池,确保所有任务都被执行完毕。

注意:这里销毁的时候保证知道数组中有多少个有效的线程ID,进而销毁。

可在结构体中添加pool->num_threads ,即用来保存线程池中线程数量的成员变量,应该在初始化线程池时进行设置,并在后续操作中根据需要使用,则不会出现在销毁时越界或死锁。

这个示例展示了如何使用线程池来管理并发执行的任务,以及如何通过互斥锁和条件变量来实现线程安全的任务队列操作。

相关文章:

【线程系列之五】线程池介绍C语言

一、基本概念 1.1 概念 线程池&#xff08;Thread Pool&#xff09;是一种基于池化技术管理线程的机制&#xff0c;旨在减少线程创建和销毁的开销&#xff0c;提高系统资源的利用率&#xff0c;以及更好地控制系统中同时运行的线程数量。线程池通过预先创建一定数量的线程&am…...

【学习css3】使用flex和grid实现等高元素布局

过往的实现方法是使用浮动加计算布局来实现&#xff0c;当flex和grid问世时&#xff0c;这一切将变得简单起来 一、简单的两列实现 1、先看页面效果 2、css代码 .container {padding: 10px;width: 100ch;margin: 0 auto;box-shadow: inset 0 0 0 2px #ccc;}.column {margin: 2…...

如何防止Eclipse格式化程序在行注释开头插入空格

格式化前&#xff1a; //foo bar 格式化后&#xff1a; // foo bar 这种看着不是很舒服。如果不让格式化时自动在注释符后面插入空格呢&#xff1f; 要在Eclipse中进行代码格式化时防止在行注释&#xff08;‌//&#xff09;‌后面自动增加空格&#xff0c;‌可以通过调整…...

Nextjs 调用组件内的方法

在 Next.js 中&#xff0c;如果你想从一个组件外部调用组件内部的方法&#xff0c;可以使用 React 的 useRef 钩子来引用组件实例并调用其方法。这种方法主要适用于类组件&#xff0c;但也可以用于函数组件&#xff0c;通过将方法暴露在 ref 对象上。 以下是一个示例&#xff…...

ip地址是电脑还是网线决定的

在数字化时代的浪潮中&#xff0c;网络已经成为了我们日常生活和工作不可或缺的一部分。当我们谈论网络时&#xff0c;IP地址无疑是一个核心的概念。然而&#xff0c;关于IP地址的分配和决定因素&#xff0c;很多人可能存在误解。有些人认为IP地址是由电脑决定的&#xff0c;而…...

Hadoop中HDFS、Hive 和 HBase三者之间的关系

HDFS&#xff08;Hadoop Distributed File System&#xff09;、Hive 和 HBase 是 Hadoop 生态系统中三个重要的组件&#xff0c;它们各自解决了大数据存储和处理的不同层面的问题。我们用大白话来解释这三个组件之间的关系&#xff1a; HDFS - 数据的仓库&#xff1a; HDFS 是…...

opencv—常用函数学习_“干货“_10

目录 二七、离散余弦变换 执行离散余弦变换 (dct) 和逆变换 (idct) 解释 实际应用 JPEG压缩示例&#xff08;简化版&#xff09; 二八、图像几何变换 仿射变换 (warpAffine 和 getAffineTransform) 透视变换 (warpPerspective 和 getPerspectiveTransform) 旋转变换 (g…...

Jmeter二次开发Demo

Jmeter二次开发Demo 前言 在上一集&#xff0c;我们已经完成了JMX脚本的分析&#xff0c;大致了解了JMX脚本的基本元素。 那么在这一集&#xff0c;我们将会介绍一下Jmeter二次开发的Demo。 Demo代码 那么话不多说&#xff0c;我们就直接上代码。 public class TestStress…...

MongoDB综合实战篇(超容易)

一、题目引入 在MongoDB的gk集合里插入以下数据&#xff1a; 用语句完成如下功能&#xff1a; &#xff08;1&#xff09;查询张三同学的成绩信息 &#xff08;2&#xff09;查询李四同学的语文成绩 &#xff08;3&#xff09;查询没有选化学的同学 &#xff08;4&#xf…...

框架设计MVVM

重点&#xff1a; 1.viewmodel 包含model 2.view包含viewmodel,通过驱动viewmodel去控制model的数据和业务逻辑 // Test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //#include <iostream> #include <vector>using namespace std;#p…...

RK3399基础部分

1.RK3399介绍 基础特性&#xff1a; 高达1.8GHz的双核Cortex-A72 四核Cortex-A53高达1.4GHz NPU高达3.0TOPS Mali-T860MP4 GPU 双通道DDR3/DDR3L/LPDDR3/LPDDR4 4K超高清H265/H264/VP9 HDR10/HLG H264编码器 双MIPI CSI和ISP USB Type-CGPU: 图形处理器&#xff08;英语&…...

linux高级编程(广播与组播)

广播与组播&#xff1a; 广播&#xff1a; 局域网&#xff0c;一个人发所有人都能收&#xff08;服务器找客户端&#xff09;&#xff0c;&#xff08;发给路由器的广播地址后后路由器自动给所有人发&#xff0c;可用于服务器找客户端&#xff09; 只能udp来做 setsocketopt…...

Andriod Stdio新建Kotlin的Jetpack Compose简单项目

1.选择 No Activity 2.选择kotlin 4.右键选择 在目录MyApplication下 New->Compose->Empty Project 出现下面的画面 Finish 完成...

Linux多线程编程-哲学家就餐问题详解与实现(C语言)

在哲学家就餐问题中&#xff0c;假设有五位哲学家围坐在圆桌前&#xff0c;每位哲学家需要进行思考和进餐两种活动。他们的思考不需要任何资源&#xff0c;但进餐需要使用两根筷子&#xff08;左右两侧各一根&#xff09;。筷子是共享资源&#xff0c;哲学家们在进行进餐时需要…...

从C向C++18——演讲比赛流程管理系统

一.项目需求 1.比赛规则 学校举行一场演讲比赛&#xff0c;共有12个人参加。比赛共两轮&#xff0c;第一轮为淘汰赛&#xff0c;第二轮为决赛。每名选手都有对应的编号&#xff0c;如 10001~ 10012比赛方式&#xff1a;分组比赛&#xff0c;每组6个人&#xff1b;第一轮分为两…...

QThread和std::thread

在 Qt 中&#xff0c; 我们经常会用到多线程&#xff0c;这时候就需要纠结是使用 Qt 的 QThread 还是使用 C 标准库的 std::thread。 这里记录一下我自己的理解&#xff0c;先介绍一下 QThread 和 std::thread 的使用方法&#xff0c;对比一下他们的不同&#xff0c;最后说一下…...

LeetCode 算法:组合总和 c++

原题链接&#x1f517;&#xff1a;组合总和 难度&#xff1a;中等⭐️⭐️ 题目 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 …...

【两大3D转换SDK对比】HOOPS Exchange VS. CAD Exchanger

在现代工业和工程设计领域&#xff0c;CAD数据转换工具是确保不同软件系统间数据互通的关键环节。HOOPS Exchange和CAD Exchanger是两款备受关注的工具&#xff0c;它们在功能、支持格式、性能和应用场景等方面有着显著差异。 本文将从背景、支持格式、功能和性能、应用场景等…...

Openerstry + lua + redis根据请求参数实现动态路由转发

文章目录 一、需求分析二、准备1、软件安装2、redis-lua封装优化 三、实现1、nginx.conf2、dynamic.lua注意 3、准备两个应用4、访问nginx 四、参数直接传要代理的地址端口 一、需求分析 根据用户访问url的参数&#xff0c;将请求转发到对应指定IP的服务器上。 二、准备 1、…...

数字名片-Pushmall 智能AI数字名片7月更新计划

[数字名片]-商务营销推广助手7月更新计划 数字名片-商务营销推广助手7月更新计划 **2024年 6月完成模块开发优化****实现SaaS框架业务 1、智能名片&#xff1a;创建个人名片、企业名片、商机管理。 2、人脉商圈&#xff1a;附近人脉、就近群脉、好友名片。 3、企微社群&…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...