条件变量、线程池以及线程的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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringMVC 的请求流程(高频面试题)

文章目录 SpringMVC是什么&#xff0c;好处请求流程 SpringMVC 是什么&#xff0c;好处 Spring MVC 是Spring上最重要的框架。它是Web 框架&#xff0c;按照MVC 的模式&#xff0c;实现代码的解耦。Model 业务层、View 视图层、Controller 控制层。 将view层的请求数据在con…

【Android】在电脑连接操控手机上的Termux终端实现步骤

在Android手机上有一个Termux APP&#xff0c;可运行类似 Linux 终端的模拟器&#xff0c;想要学习Linux命令操作的话&#xff0c;可以用来练习&#xff0c;手机上输入命令不方便的话&#xff0c;可以用电脑连接手机终端输入命令练习的&#xff0c;来看看怎么实现连接。 文章目…

Advances in Deep Concealed Scene Understanding (伪装场景理解综述解读)

论文地址&#xff1a;https://link.springer.com/article/10.1007/s44267-023-00019-6 摘要 伪装场景理解是一个热门的计算机视觉课题&#xff0c;旨在感知展示伪装的物体&#xff0c;当前技术和应用的繁荣需要最新的研究调查&#xff0c;这可以帮助研究人员更好的了解全球CS…

【大数据】Flink 架构(四):状态管理

Flink 架构&#xff08;四&#xff09;&#xff1a;状态管理 1.算子状态2.键值分区状态3.状态后端4.有状态算子的扩缩容4.1 带有键值分区状态的算子4.2 带有算子列表状态的算子4.3 带有算子联合列表状态的算子4.4 带有算子广播状态的算子 在前面的博客中我们指出&#xff0c;大…

做成任何事情的方法

怎么样把一件事情做成功呢&#xff1f; 传统的方法可能会告诉你&#xff1a;你首先要有一个目标&#xff0c;目标要明确&#xff0c;可量化&#xff0c;要遵循 SMART 原则&#xff1b;然后再把目标拆成一个个小目标&#xff0c;先实现一个小目标&#xff0c;再实现一个小目标&a…

研发日记,Matlab/Simulink避坑指南(八)——else if分支结构Bug

文章目录 前言 背景介绍 问题描述 分析排查 解决方案 总结归纳 前言 见《研发日记&#xff0c;Matlab/Simulink避坑指南(三)——向上取整Bug》 见《研发日记&#xff0c;Matlab/Simulink避坑指南(四)——transpose()转置函数Bug》 见《研发日记&#xff0c;Matlab/Simuli…

Java强训day7(选择题编程题)

选择题 public class Test01{private static int x 100;public static void main(String[] args) {Test01 hs1 new Test01();hs1.x;Test01 hs2 new Test01();hs2.x;hs1new Test01();hs1.x;Test01.x--;System.out.println("x"x);} }public class Test01{private …

高级IO之epoll模型

一、epoll模型介绍 epoll是Linux内核为处理大批量文件描述符而作了改进的poll&#xff0c;是Linux下多路复用IO接口select/poll的增强版本&#xff0c;用于监视一个或多个文件描述符&#xff0c;以查看它们是否可以进行读取、写入或异常处理。它能够显著提高程序在大量并发连接…

C语言系列-浮点数在内存中的存储

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” 目录 浮点数在内存中的存储 浮点数的存储 浮点数存的过程 浮点数取的过程 题目解析 浮点数在内存中的存储 常见的浮点数&#xff1a;3.14159.1E10等&#xff0c;浮点数家族包括&…

微信开发者工具 git 拉取 failed invalid authentication scheme

微信开发者工具 git 拉取 failed invalid authentication scheme 拉取代码时报错,无效身份认证 解决方案: 1.检查git地址是否正常 2.检查git用户名密码是否正确

【Vue2 + ElementUI】更改el-select的自带的下拉图标为倒三角,并设置相关文字颜色和大小

效果图 实现 <template><div class"search_resources"><div class"search-content"><el-select class"search-select" v-model"query.channel" placeholder"请选择" change"handleResource&q…

【Matlab】音频信号分析及FIR滤波处理——凯泽(Kaiser)窗

一、前言 1.1 课题内容: 利用麦克风采集语音信号(人的声音、或乐器声乐),人为加上环境噪声(窄带)分析上述声音信号的频谱,比较两种情况下的差异根据信号的频谱分布,选取合适的滤波器指标(频率指标、衰减指标),设计对应的 FIR 滤波器实现数字滤波,将滤波前、后的声音…

258:vue+openlayers加载mapbox-style的地图

第258个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers中添加mapbox地图,跟之前的不同处理方式是,这里采用了ol-mapbox-style插件来加载mapbox地图。具体请参考源代码和API。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果 文章目录 示…

基于Micropython利用ESP32-C3墨水屏电子时钟方法

本篇笔记介绍一下我们设计制作的墨水屏时钟。 1、所需硬件 1&#xff09;合宙的ESP32-C3&#xff1a; 2&#xff09;电子价签拆出来的2.9寸墨水屏&#xff1a; ——电子价签型号为&#xff1a;Stellar-L&#xff0c;墨水屏型号为&#xff1a;E029A01。 3&#xff09;自己设计…

Linux-ROS学习之旅(一)

##本人使用的是双系统&#xff0c;noetic版本&#xff0c;学习ROS初衷是学习控制机械臂&#xff0c;具体下载方法见B站&#xff0c;观看的教程是古月居早年的教学视频&#xff0c;和ROS_wiki&#xff1a;ROS/Tutorials - ROS Wiki ##下一篇文章有具体的实例&#xff0c;但是所用…

OJ_阶乘的和

题干 c语言实现 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<vector> #include<set> using namespace std;int main() {vector<int> factorialArr;//把0&#xff01;放入数组factorialArr.push_back(1);int curFactorial 1;for (in…

《合成孔径雷达成像算法与实现》Figure5.16

clc clear close all距离向参数 R_eta_c 20e3; % 景中心斜距 Tr 25e-6; % 发射脉冲时宽 Kr 0.25e12; % 距离向调频率 Fr 7.5e6; % 距离向采样率 Nrg 256; % 距离线采样点数 Bw abs(Kr*Tr); …

uniapp,页面当有按钮的时候,可以做一个动态的效果

效果&#xff1a; 这个是当点着按钮的时候没有松开按钮的效果&#xff08;没有阴影&#xff09; 这个是当松开按钮的效果&#xff08;有阴影&#xff09; 原理讲解&#xff1a; 这段代码实现的业务逻辑是在一个Vue组件中控制“现金”按钮的阴影效果。具体来说&#xff0c;它通…

系统架构17 - 软件工程(5)

软件工程 软件测试测试原则测试方法静态测试动态测试黑盒测试白盒测试灰盒测试自动化测试 测试阶段单元测试集成测试系统测试性能测试验收测试其它测试AB测试Web测试链接测试表单测试 测试用例设计黑盒测试用例白盒测试用例 系统维护遗留系统系统转换转换方式数据转换与迁移 评…

C++力扣题目416--分割等和子集 1049--最后一块石头的重量II

416. 分割等和子集 力扣题目链接(opens new window) 题目难易&#xff1a;中等 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200 示例 1: 输入: […