【并发程序设计】10.线程池

10.线程池

通俗的讲就是一个线程的池子,可以循环的完成任务的一组线程集合

必要性

我们平时创建一个线程,完成某一个任务,等待线程的退出。但当需要创建大量的线程时,假设T1为创建线程时间,T2为在线程任务执行时间,T3为线程销毁时间,当 T1+T3 > T2,这时候就不划算了,使用线程池可以降低频繁创建和销毁线程所带来的开销,任务处理时间比较短的时候这个好处非常显著。

线程池的基本结构

  1. 任务队列,存储需要处理的任务,由工作线程来处理这些任务
  2. 线程池工作线程,它是任务队列任务的消费者,等待新任务的信号

线程池实现步骤

  1. 创建线程池的基本结构:

    typedef struct Task;  //任务队列链表
    typedef struct ThreadPool;  //线程池结构体
    
  2. 线程池的初始化:

    pool_init()
    {/*创建一个线程池结构*//*实现任务队列互斥锁和条件变量的初始化*//*创建n个工作线程*/
    }
    
  3. 线程池添加任务:

    pool_add_task
    {/*判断是否有空闲的工作线程*//*给任务队列添加一个节点*//*给工作线程发送信号newtask*/
    }
    
  4. 实现工作线程:

    workThread
    {while(1){/*等待newtask任务信号*//*从任务队列中删除节点*//*执行任务*/}
    }
    
  5. 线程池的销毁

    pool_destory
    {/*删除任务队列链表所有节点,释放空间*//*删除所有的互斥锁条件变量*//*删除线程池,释放空间*/
    }
    

示例

20个任务,4个工作线程

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>#define POOL_NUM 4//线程池内工作线程数量
/*1.基本结构*/
typedef struct Task//任务链表结构体
{void *(*func)(void *arg);//任务(函数指针)void *arg;//函数参数struct Task *next;//下一个任务节点
}Task;typedef struct ThreadPool//线程池结构体
{pthread_mutex_t taskLock;//任务互斥锁pthread_cond_t newTask;//条件变量pthread_t tid[POOL_NUM];//线程IDTask *queue_head;//任务链表头int busywork;//忙碌线程数
}ThreadPool;ThreadPool *pool;//线程池变量void *realwork(void *arg);
void pool_init();
void pool_add_task(int arg);
void *workThread(void *arg);
void pool_destory();/*2.初始化*/
void pool_init()//线程池初始化
{int i;pool = malloc(sizeof(ThreadPool));//申请空间pthread_mutex_init(&pool->taskLock,NULL);//互斥锁初始化pthread_cond_init(&pool->newTask,NULL);//条件变量初始化pool->queue_head = NULL;//任务链表头节点pool->busywork=0;//忙碌线程数//创建线程for(i=0;i<POOL_NUM;i++){pthread_create(&pool->tid[i],NULL,workThread,NULL);}
}/*3.添加任务*/
void *realwork(void *arg)//“任务”
{printf("work%d\n",(int)arg);
}
void pool_add_task(int arg)//添加任务节点
{Task *newTask;//定义新任务节点pthread_mutex_lock(&pool->taskLock);//1上锁//等待任务链表空位while(pool->busywork>=POOL_NUM)//没有空闲线程{pthread_mutex_unlock(&pool->taskLock);//1解锁usleep(10000);//等待10mspthread_mutex_lock(&pool->taskLock);//2上锁}pthread_mutex_unlock(&pool->taskLock);//2解锁//创建新任务节点newTask = malloc(sizeof(Task));//新任务节点申请空间newTask->func =  realwork;//新任务节点的任务newTask->arg = (void *)arg;//新任务节点的参数//将新节点从链表尾部加入pthread_mutex_lock(&pool->taskLock);//3上锁Task *member = pool->queue_head;if(member==NULL)//如果头节点为空pool->queue_head = newTask;else//如果头节点不为空{while(member->next!=NULL)//找到节点尾部{member=member->next;}member->next = newTask;//将新节点加入链表}pool->busywork++;pthread_cond_signal(&pool->newTask);//条件变量通知有资源pthread_mutex_unlock(&pool->taskLock);//3解锁
}/*4.实现工作线程*/
void *workThread(void *arg)//工作线程函数
{while(1){pthread_mutex_lock(&pool->taskLock);//上锁pthread_cond_wait(&pool->newTask,&pool->taskLock);//等待新任务//从链表头部取出任务节点Task *ptask = pool->queue_head;pool->queue_head = pool->queue_head->next;pthread_mutex_unlock(&pool->taskLock);//解锁//执行任务ptask->func((void *)ptask->arg);//通过节点取出任务函数,执行//任务结束pool->busywork--;}
}
/*5.线程池销毁*/
void pool_destory()
{Task *head;while(pool->queue_head!=NULL){head = pool->queue_head;pool->queue_head = pool->queue_head->next;free(head);//释放节点空间}pthread_mutex_destroy(&pool->taskLock);//销毁互斥锁pthread_cond_destroy(&pool->newTask);//销毁条件变量free(pool);//释放线程池空间
}
int main()
{int i;//启动线程池pool_init();sleep(2);//添加任务for(i=1;i<=20;i++){pool_add_task(i);}sleep(2);pool_destory();
}

GDB调试多线程程序

gcc编译.c程序时加入 -g 参数

gcc -g xxx.c
gdb a.out #进入调试

相关指令:

  • (gdb) info thread
    • 显示线程
  • (gdb) thread [id]
    • 切换线程
  • (gdb) break location thread [id]
    • 为特定线程设置断点
  • (gdb) b [行号]
    • 在行号处设置断点
  • (gdb) b [行号] thread [id]

  • (gdb) set scheduler-locking on/off
    • 设置线程锁
    • on:其他线程会暂停。可以单独调试一个线程
  • (gdb) p threadName
    • 显示当前线程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/17314.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

`var functionName = function() {}` 与 `function functionName() {}` 的区别探讨

本文将详细讲解和分析 JavaScript 中定义函数的两种标准形式:函数表达式和函数声明。理解这两种形式以及它们在不同情况下的行为,对于编写高效、可维护的代码非常重要。 函数声明 (Function Declaration) 函数声明是一种较为常见的定义函数的方式,语法如下: function x(…

Stable Diffusion AI绘画:从提示词到模型出图的全景指南

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

delphi,url中文编码

1、参考https://www.cnblogs.com/lucken2000/p/17582930.html 2、引用 HttpApp或IdURI 3、 function UrlEncodeUTF8(stInput : widestring): string; consthex : array[0..255] of string (%00, %01, %02, %03, %04, %05, %06, %07,%08, %09, %0a, %0b, %0c, %0d, %0e, %0f,…

「小明赠书活动」第四期《Java开发坑点解析:从根因分析到最佳实践》

目录 ⭐️ 赠书 - 《Java开发坑点解析&#xff1a;从根因分析到最佳实践》 参 加 活 动 方 式 见 文 末 ⭐️内容简介 -《Java开发坑点解析&#xff1a;从根因分析到最佳实践》 ⭐️阅读建议 -《Java开发坑点解析&#xff1a;从根因分析到最佳实践》 ⭐️《Java开发坑…

新人学习笔记之(JavaScript循环)

目录 一、循环 1.循环的目的 2.js中的循环 二、for循环 1.在程序中&#xff0c;一组被重复执行的语句被称之为循环体&#xff0c;能否继续重复执行&#xff0c;取决于循环的终止条件&#xff0c;由循环体及循环的终止条件组成的语句&#xff0c;被称之为循环语句 2.for循环重…

如果你有电脑,请狠下心来你死磕这6门技能

在这个日新月异、竞争激烈的时代&#xff0c;掌握一门技能已经远远不够。为了在职场中脱颖而出&#xff0c;我们需要不断地学习和提升自己的能力。你的电脑除了用来办公做文档外&#xff0c;还可以用它自学这些技能&#xff0c;让你轻松月入过万&#xff01; 1&#xff1a;编程…

JS基础知识 —— AOP面向切片开发

前言&#xff1a;学习笔记&#xff01; function test1() {console.log("-----------------");console.log(1);console.log("");}function test2() {console.log("-----------------");console.log(2);console.log("");}function test…

C++容器之向量(std::vector)

目录 1 概述2 使用实例3 接口使用3.1 construct3.2 assigns3.3 iterators3.4 capacity3.5 rezize3.6 reserve3.7 shrink_to_fit3.8 access3.9 assign3.10 push_back3.11 pop_back3.12 insert3.13 erase3.14 swap3.15 clear3.16 emplace3.17 emplace_back3.18 get_allocator1 概…

记录一次内存取证

1.情景复现 我姐姐的电脑坏了。我们非常幸运地恢复了这个内存转储。你的工作是从系统中获取她所有的重要文件。根据我们的记忆&#xff0c;我们突然看到一个黑色的窗口弹出&#xff0c;上面有一些正在执行的东西。崩溃发生时&#xff0c;她正试图画一些东西。这就是我们从崩溃…

get_mbutton

get_mbutton (3600, Row, Column, Button) while (Button 1) get_mposition (3600, Row, Column, Button) get_mbutton( : : WindowHandle : Row, Column, Button) 等到按下鼠标按钮并返回点击坐标。 get_mbutton_sub_pix( : : WindowHandle : Row, Column, Butt…

ArrayBuffer内存格式相互转换 ArrayBuffer转化16进制 16进制转字符串 pcm转wav音频

一&#xff1a; pcm转wav音频 针对于音频格式的转换 const encodeWAV (samples, numChannels, sampleRate) > {var buffer new ArrayBuffer(44 samples.byteLength)var view new DataView(buffer)/* RIFF identifier */writeString(view, 0, RIFF)/* RIFF chunk length …

探索数组的最大值与最小值:从基础到进阶

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;数组的奥秘 二、基础操作&#xff1a;查找数组的最大值和最小值 三、…

怎样消除工人们对六西格玛培训的抵触情绪?

近年来&#xff0c;企业为了提高产品质量、优化生产流程&#xff0c;纷纷引入了六西格玛管理方法。然而&#xff0c;在实施过程中&#xff0c;不少企业却遭遇了工人们对六西格玛培训的抵触情绪。这种情绪的存在不仅阻碍了六西格玛的推广和应用&#xff0c;也影响了企业的整体运…

邦芒职场:揭秘影响你职场收入的九大细节

在职场这个大舞台上&#xff0c;微小的细节往往能决定你收入的多少。以下九大细节&#xff0c;是你职场成功的关键&#xff0c;不容忽视。 1. 形象塑造 在这个注重第一印象的时代&#xff0c;良好的形象是你的第一张名片。精致的妆容、得体的着装&#xff0c;不仅能为你加分&a…

packstack一键部署OpenStack云平台

OpenStack一键部署 文章目录 OpenStack一键部署资源列表基础环境一、基础环境配置1.1、配置时间同步1.2、配置网络1.3、添加hosts绑定1.4、更新系统并安装常用软件 二、使用packstack一键部署OpenStack2.1、Train版YUM源安装2.2、Packstack软件包安装2.3、Packstack一键部署Ope…

electron-01 基础及NPM相关配置

electron基础 结构 ChromiumNode.jsNative apis 工作流程 启动APP主进程创建windowWin加载界面操作 主进程 package.json中main属性对应的文件一个应用对应一个主进程只有主进程可以进行GUI的API操作 渲染进程 windows中展示的界面通过渲染进程表现一个应用可以有多个渲…

Unity 生成模版代码

1、创建模版代码文本 using System.Collections; using System.Collections.Generic; using UnityEngine;public class ClassNameScritpItem : MonoBehaviour {public GameObject go;// Start is called before the first frame updatevoid Start(){go new GameObject();}// …

LeetCode115:不同的子序列

题目描述 给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数&#xff0c;结果需要对 109 7 取模。 代码 /*dp[i][j]&#xff1a;以i为结尾的s中有以j为尾的t的个数递推公式&#xff1a;当s[i - 1] 与 t[j - 1]相等时&#xff0c;dp[i][j]可以有两…

工业LED显示屏汉字乱码方式的解决

目录 研究背景 解决方法 原因分析 尝试的解决方法 本质原因 写在最后 研究背景 想实现LED显示屏数字、字母、汉字均能正常显示的效果&#xff08;效果如下&#xff09;。在将UTF-8改为GB2312 编码之前&#xff0c;数字和字母不乱&#xff0c;但是汉字会乱码。 解决方法 1…

Rust的高效易用日志库—tklog

很多人习惯于python&#xff0c;go等语言基础工具库的简单易用&#xff1b;在使用rust时&#xff0c;可能感觉比较麻烦&#xff0c;类似日志库这样的基础性工具库。tklog提供用法上&#xff0c;非常类似python等Logger的日志库用法&#xff0c;用法简洁&#xff1b;基于rust的高…