条件变量、线程池以及线程的GDB调试学习笔记

目录

一、条件变量

二、线程池概念和实现

三、线程的GDB调试


一、条件变量

应用场景:生产者消费者问题,是线程同步的一种手段。

必要性:为了实现等待某个资源,让线程休眠,提高运行效率

使用步骤

        初始化

  • 静态初始化                        

         pthread_cond_t   cond = PTHREAD_COND_INITIALIZER;      //初始化条件变量

         pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;  //初始化互斥量

  • 动态初始化 

         pthread_cond_init(&cond);

        生产者线程

  •  pthread_mutex_lock(&mutex);
  • 开始生产资源
  • pthread_cond_signal(&cond);    //通知一个消费线程

或者

  • pthread_cond_broadcast(&cond); //广播通知多个消费线程
  • pthread_mutex_unlock(&mutex);

        消费者线程

  •  pthread_mutex_lock(&mutex);
  • while (如果没有资源){   //防止惊群效应

    pthread_cond_wait(&cond, &mutex);

    }

  • 有资源了,消费资源

  • pthread_mutex_unlock(&mutex);

示例代码:

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
//初始化条件变量
pthread_cond_t hasTaxi = PTHREAD_COND_INITIALIZER;
//初始化互斥量
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;struct taxi{struct taxi *next;int num;
};struct taxi *Head = NULL;
void *taxiarv(void *arg)
{printf("taxi arrived thread\n");pthread_detach(pthread_self());struct taxi *tx;int i = 1;while (1){tx = malloc(sizeof(struct taxi));tx->num = i;printf("taxi %d comming\n",i);i++;pthread_mutex_lock(&lock);tx->next = Head;Head = tx;pthread_cond_signal(&hasTaxi);//通知消费者车来了pthread_mutex_unlock(&lock);sleep(1);}pthread_exit(0);
}
void *takeTaxi(void *arg)
{printf("take taxi thread\n");pthread_detach(pthread_self());struct taxi *tx;while (1){pthread_mutex_lock(&lock);while (Head == NULL)//如果没有资源{//放置惊群效应pthread_cond_wait(&hasTaxi,&lock);}//有资源了,消费资源tx = Head;Head = tx->next;printf("Take taxi %d\n",tx->num);free(tx);pthread_mutex_unlock(&lock);}pthread_exit(0);
}
int main()
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,taxiarv,NULL);pthread_create(&tid2,NULL,takeTaxi,NULL);while (1){sleep(1);} 
}

 运行结果:

注意:

  1. pthread_cond_wait(&cond, &mutex),在没有资源等待是是先unlock 休眠,等资源到了,再lock,所以pthread_cond_wait 和 pthread_mutex_lock 必须配对使用。

pthread_mutex_unlock

如果资源没有来sleep

如果来了

pthread_mutex_lock 

        2. 如果pthread_cond_signal或者pthread_cond_broadcast 早于 pthread_cond_wait ,则有可能会丢失信号。

        3. pthead_cond_broadcast 信号会被多个线程收到,这叫线程的惊群效应。所以需要加上判断条件while循环。即代码中的 while (Head == NULL)//如果没有资源。

 二、线程池概念和实现

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

        打个比喻:比如一个公司招人做项目,招到一个人,做完项目就把这个人解雇,然后又来项目就再招人再解雇,招聘和解雇流程繁琐消耗大量时间,而线程池呢就相当于招聘到一个人不解雇,来一个项目做完等着,来第二个项目继续做,就剩去了解雇和再招聘的时间。 

必要性:我们平时创建一个线程,完成某一个任务,等待线程的退出。但当需要创建大量的线程时,假设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
    {
            删除任务队列链表所有节点,释放空间
            删除所有的互斥锁条件变量
            删除线程池,释放空间

 示例代码:

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define POOL_NUM 10
//任务队列链表
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];//定义10个线程Task *queue_head;//拿到任务的头部int busywork;//表示有几个任务工作
}ThreadPool;
ThreadPool *pool;
//工作线程
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(ptask->arg); //函数真正的运行pool->busywork--;}}
void *realwork(void *arg)
{printf("Finish work %d\n",(int)arg);
}
//向线程池添加任务
void pool_add_task(int arg)
{Task *newTask;//访问线程池临界资源,所以要加锁pthread_mutex_lock(&pool->taskLock);while(pool->busywork>=POOL_NUM){pthread_mutex_unlock(&pool->taskLock);usleep(10000);//休眠的时候要把锁释放掉pthread_mutex_lock(&pool->taskLock);}pthread_mutex_unlock(&pool->taskLock);newTask = malloc(sizeof(Task));newTask->func = realwork; //函数指针初始化newTask->arg = arg;pthread_mutex_lock(&pool->taskLock);Task *member = pool->queue_head;if(member == NULL){pool->queue_head = newTask;}else{while (member->next!=NULL)//遍历链表,找到末尾{member = member->next;}member->next = newTask;//将newTask放在链表的尾部}pool->busywork++;pthread_cond_signal(&pool->newTask);pthread_mutex_unlock(&pool->taskLock);
}
//线程池的初始化
void pool_init()
{pool = malloc(sizeof(ThreadPool));//对线程池分配一个空间pthread_mutex_init(&pool->taskLock,NULL);//对任务进程初始化pthread_cond_init(&pool->newTask,NULL);//对条件变量进行初始化pool->queue_head = NULL;pool->busywork = 0; for(int i = 0; i<POOL_NUM; i++){pthread_create(&pool->tid[i],NULL,workThread,NULL);}
}
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()
{pool_init();sleep(1);for(int i=1;i<=20;i++){pool_add_task(i);}sleep(5);pool_destory();
}

运行结果:

执行20个任务,而线程池的容量是10,所以会有10个在等待着执行。

三、线程的GDB调试

示例代码:

#include <pthread.h>
#include <stdio.h>void *testThread(void *arg)
{char *threadName = (char *)arg;printf("Current running %s\n",threadName);printf("aaaaaa\n");printf("bbbbbb\n");pthread_exit(0);
}
int main()
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,testThread,"thread1");pthread_create(&tid2,NULL,testThread,"thread2");pthread_join(tid1,NULL);pthread_join(tid2,NULL);
}

正常运行结果:

显示线程:info thread

切换线程:thread id

将断点打在第6行

 

GDB设置线程锁:

——set scheduler-locking on/off

GDB为特定线程设置断点

——break location thread id

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

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

相关文章

深入理解C语言(3):自定义类型详解

文章主题&#xff1a;结构体类型详解&#x1f30f;所属专栏&#xff1a;深入理解C语言&#x1f4d4;作者简介&#xff1a;更新有关深入理解C语言知识的博主一枚&#xff0c;记录分享自己对C语言的深入解读。&#x1f606;个人主页&#xff1a;[₽]的个人主页&#x1f3c4;&…

前端使用阿里Oss

前言&#xff1a;有时候为了减少宽带和服务器压力等等&#xff0c;就直接给前端操作oss实习文件上传了官方文档 安装 npm i ali-oss 因为我们是js可以使用node jdkconst OSS require(ali-oss);// 初始化OSS客户端。请将以下参数替换为您自己的配置信息。 const client new O…

Rust循环和函数

下面聊聊以下主题&#xff1a; 基于条件的分支循环函数属性测试 基于条件的分支 基于条件的分支&#xff0c;可以通过常见的 if、if else 或 if else if else 构造来完成&#xff0c;例如下面的示例&#xff1a; fn main() { let dead false; let health 48; if dead { p…

《汇编语言:基于linux环境》补码研究

刚开始我使用&#xff0c;如下命令编译&#xff0c;链接程序。 nasm -f elf64 -g -F stabs sandbox.asmld -o sandbox sandbox.ogdb sandbox当我运行 sandbox 时&#xff0c;它会正常运行&#xff0c;但 gdb 无法显示任何源代码。为什么&#xff1f;当我在 gdb 中尝试 run 时&a…

鸿蒙(ArkUI)开发:实现二级联动

场景介绍 列表的二级联动&#xff08;Cascading List&#xff09;是指根据一个列表&#xff08;一级列表&#xff09;的选择结果&#xff0c;来更新另一个列表&#xff08;二级列表&#xff09;的选项。这种联动可以使用户根据实际需求&#xff0c;快速定位到想要的选项&#…

粒子群优化算法(Particle Swarm Optimization,PSO)求解基于移动边缘计算的任务卸载与资源调度优化(提供MATLAB代码)

一、优化模型介绍 移动边缘计算的任务卸载与资源调度优化原理是通过利用配备计算资源的移动无人机来为本地资源有限的移动用户提供计算卸载机会&#xff0c;以减轻用户设备的计算负担并提高计算性能。具体原理如下&#xff1a; 任务卸载&#xff1a;移动边缘计算系统将用户的计…

Windows11搭建GPU版本PyTorch环境详细过程

Anaconda安装 https://www.anaconda.com/ Anaconda: 中文大蟒蛇&#xff0c;是一个开源的Python发行版本&#xff0c;其包含了conda、Python等180多个科学包及其依赖项。从官网下载Setup&#xff1a;点击安装&#xff0c;之后勾选上可以方便在普通命令行cmd和PowerShell中使用…

BGP同步规则

BGP同步规则&#xff1a;开启同步下&#xff0c;从IBGP收到一条路由不会传给任何EBGP邻居(实验效果IBGP邻居和EBGP邻居都不传)&#xff0c;除非从自身的IGP中也学到这条路由。目的是防止AS内部出现路由黑洞&#xff0c;向外部通告了一个本AS不可达的虚假的路由。 同步规则只影响…

qt学习:http+访问百度智能云api实现车牌识别

目录 登录到百度智能云&#xff0c;找到文字识别 完成操作指引 开通 查看车牌识别的api文档 ​编辑​编辑 查看自己应用的api key 查看回应的数据格式 编程步骤 ui界面编辑 添加模块&#xff0c;头文件和定义变量 新建两个类&#xff0c;一个图像Image类&#xff0c…

Day01-变量和数据类型课后练习(输出你最想说的一句话,定义所有基本数据类型的变量和字符串变量,用合适类型的变量存储个人信息并输出,定义圆周率PI,简答题)

参考答案博客链接跳转 文章目录 1、输出你最想说的一句话&#xff01;2、定义所有基本数据类型的变量和字符串变量3、用合适类型的变量存储个人信息并输出4、定义圆周率PI5、简答题 1、输出你最想说的一句话&#xff01; 编写步骤&#xff1a;List item定义类 Homework1&…

【嵌入式移植】5、U-Boot源码分析2—make nanopi_neo2_defconfig

U-Boot源码分析2—make nanopi_neo2_defconfig 1 概述2 nanopi_neo2_defconfig3 编译过程分析3.1 编译目标3.2 scripts_basic3.2.1 prefix src定义3.2.2 PHONY3.2.3 __build3.2.4 fixdep3.3 objscripts/kconfig 1 概述 上一章中&#xff0c;对Makefile相关源码进行了初步分析&…

从零开始:CentOS系统下搭建DNS服务器的详细教程

前言 如果你希望在CentOS系统上建立自己的DNS服务器,那么这篇文章绝对是你不容错过的宝藏指南。我们提供了详尽的步骤和实用技巧,让你能够轻松完成搭建过程。从安装必要的软件到配置区域文件,我们都将一一为你呈现。无论你的身份是运维人员,还是程序员,抑或是对网络基础设…

服务端开发小记02——Maven

这里写目录标题 Maven简介Maven在Linux下的安装Maven常用命令 Maven简介 Apache Maven Project是一个apache的开源项目&#xff0c;是用于构建和管理Java项目的工具包。 用Maven可以方便地创建项目&#xff0c;基于archetype可以创建多种类型的java项目&#xff1b;Maven仓库…

5G_RACH(一)

什么是RACH RACH 代表 Random Access Channel。这是开机时UE发给eNB的第一条消息。 为什么选择RACH &#xff1f;&#xff08;RACH 的功能是什么&#xff1f; 当你第一次听到RACH或RACH Process这个词时&#xff0c;你脑海中浮现的第一个问题是“为什么是RACH&#xff1f;”…

05 Redis之Benchmark+简单动态字符串SDS+集合的底层实现

3.8 Benchmark Redis安装完毕后会自动安装一个redis-benchmark测试工具&#xff0c;其是一个压力测试工具&#xff0c;用于测试 Redis 的性能。 src目录下可找到该工具 通过 redis-benchmark –help 命令可以查看到其用法 3.8.1 测试1 3.9 简单动态字符串SDS 无论是 Redis …

网络通信实现

【 一 】网络通信实现 【 1 】实现网络通信的四要素 本机的ip地址 子网掩码 网关的IP地址 DNS的IP地址( 域名系统) DNS服务器是指提供域名解析服务的服务器。它负责将域名转换为相应的IP地址&#xff0c;以便计算机可以通过IP地址与其他设备进行通信。 通过使用DNS服务器…

【C语言】linux内核ipoib模块 - ipoib_netdev_ops_pf结构

一、ipoib_netdev_ops_pf结构 static const struct net_device_ops ipoib_netdev_ops_pf {.ndo_init ipoib_ndo_init,.ndo_uninit ipoib_ndo_uninit,.ndo_open ipoib_open,.ndo_stop ipoib_stop,.ndo_change_mtu ipoib_change_mtu,.ndo_…

java servlet勤工助学家教管系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java servlet 勤工助学家教管系统是一套完善的java web信息管理系统 serlvetdaobean mvc 模式开发 &#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myecli…

绝地求生:【PC】开发者日志:竞技比赛地图轮换

各位玩家大家好&#xff0c;欢迎收看闲游盒本期开发者日志。 今天闲游盒想和大家分享一下2024年竞技比赛地图轮换的几项主要改动。 从第28赛季第1轮更新&#xff08;2月&#xff09;开始&#xff0c;竞技比赛的地图阵容中将包含所有8x8尺寸的地图&#xff0c;在电竞赛事中出场…

SpringBoot-yml文件的配置与读取

配置 值前边必须要有空格&#xff0c;作为分隔符 使用空格作为缩进表示层级关系&#xff0c;相同的层级左侧对齐 获取 使用Value(”${键名}”) 使用ConfigurationProperties(prefix "前缀") 1.前缀要与yml配置文件中的前缀一致 2.实体类的字段名与配置文件中的键名一…