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

news2024/11/17 21:26:02

目录

一、条件变量

二、线程池概念和实现

三、线程的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.coloradmin.cn/o/1417870.html

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

相关文章

【ArcGIS微课1000例】0096:dem三维块状表达(层次地形模型)

文章目录 一、DEM表达方式二、层次模型表达三、注意事项一、DEM表达方式 DEM数字高程模型的表达方式通常有以下4种: 1. 规则格网 2. 不规则三角网 3. 等高线 4. 层次地形模型 作为栅格地理数据,DEM 数据具有2.5维的特征,能够以三维表面的形式进行三维空间表达。但受其数…

幻兽帕鲁Linux私服搭建备份迁移指南

幻兽帕鲁Linux私服搭建指南 文档参考 &#xff01;&#xff01;&#xff01;说明&#xff1a;不只是幻兽帕鲁&#xff0c;后续大家想自己搭私服玩别的Steam游戏&#xff0c;大部分内容都可以做一个参考 Linux安装steamcmd Linux开服步骤 游戏配置修改 Youtobe视频教程 配…

Linux中禅道12.5一键部署安装过程笔记

1. Linux中禅道12.5一键部署安装过程笔记 文章目录 1. Linux中禅道12.5一键部署安装过程笔记1. 安装1.将安装包直接解压到/opt目录下2. Apache和Mysql常用命令3. 访问和登录禅道4. 其他 2. 访问数据库1. 网页登录数据库2. 命令行连接数据库 3. 9.2.stable版本起Linux一键安装包…

vue3前端开发,vue-router路由的配置和解释

vue3前端开发,vue-router路由的配置和解释&#xff01;为了实现本次springbootvue3.测试支付宝在线支付沙盒测试的效果&#xff0c;我们现在已经开始搭建了一个基础的vue3项目。今天这篇文章是为大家提前普及一下&#xff0c;vue-router的一些基础内容。 如图&#xff0c;我们在…

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

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

C++多线程1(复习向笔记)

创建线程以及相关函数 当用thread类创建线程对象绑定函数后&#xff0c;该线程在主线程执行时就已经自动开始执行了,join起到阻塞主线程的作用 #include <iostream> #include <thread> #include <string> using namespace std; //测试函数 void printStrin…

实习日志7

1.试试pdf发票识别 1.1.添加文件类型判断 //判断文件类型 if (getFileType(imgCodeCell.getValue()) "jpg"||getFileType(imgCodeCell.getValue()) "png"||getFileType(imgCodeCell.getValue()) "jpeg"||getFileType(imgCodeCell.getValue(…

LabVIEW继电器触点接触电阻自动测试

继电器作为工业中的重要组件&#xff0c;其性能直接影响着整个生产线的可靠性和安全性。触点接触电阻是衡量继电器性能的重要参数&#xff0c;传统的测试方法效率低下且成本高昂。为了解决这些问题&#xff0c;采用LabVIEW软件&#xff0c;结合专业的硬件平台&#xff0c;实现了…

armv8 - GIC-V2 中断控制器

GIC起源 上一节中&#xff0c;粗略讲了hylicos上用的armv7上的一个通用中断控制器&#xff0c;其只支持60个中断源。但现代SoC上&#xff0c;中断系统正变得越来越复杂&#xff0c;旧的中断控制器已经无法胜任这些系统&#xff0c;主要体现在以下几点上&#xff1a; 中断源越…

(二十一)Flask之上下文管理第二篇(细细扣一遍源码)

每篇前言&#xff1a; &#x1f3c6;&#x1f3c6;作者介绍&#xff1a;【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者 &#x1f525;&#x1f525;本文已收录于Flask框架从入门到实战专栏&#xff1a;《Flask框架从入…

Linux文件管理(下)

上上篇介绍了Linux文件管理的上部分内容&#xff0c;这次继续将 Linux文件管理的剩余部分说完。内容如下。 一、查看文件内容 1、cat 命令 1.1 输出文件内容 基本语法&#xff1a; cat 文件名称主要功能&#xff1a;正序输出文件的内容。 eg&#xff1a;输出 readme.txt文…

内网安全:NTLM-Relay

目录 NTLM认证过程以及攻击面 NTLM Relay攻击 NTLM攻击总结 实验环境说明 域横向移动&#xff1a;NTLM中继攻击 攻击条件 实战一&#xff1a;NTLM中继攻击-CS转发上线MSF 原理示意图 一. CS代理转发 二. MSF架设路由 三. 适用smb_relay模块进行中继攻击 域横向移动…

Scratch:塑造孩子思维能力的魔法工具

在当今的数字化时代&#xff0c;编程已经成为一项重要的技能&#xff0c;而Scratch正是引领孩子们迈入编程世界的一扇大门。它不仅教会孩子们如何编写代码&#xff0c;更重要的是培养了孩子们的思维能力&#xff0c;为他们的成长奠定了坚实的基础。 Scratch是一款由麻省理工学…

掌握三大关键要素,玩转亚马逊测评

对于那些刚刚涉足亚马逊测评的新手小白&#xff0c;经常听到的一个警告是亚马逊平台的风控正在日益严格。尤其是对评论的管理&#xff0c;一旦稍有不慎&#xff0c;评论就可能被删除。那么&#xff0c;如何避免这种情况的发生呢&#xff1f;在进行亚马逊测评时&#xff0c;新手…

牛客周赛30

思路&#xff1a;先把x, y除以最大公约数变成最小值&#xff0c;然后同时乘以倍数cnt&#xff0c;只记录两个数都在[l,r]间的倍数。 代码&#xff1a; int gcd(int a,int b){return b ? gcd(b, a % b) : a; }void solve(){int x, y, l, r;cin >> x >> y >>…

Unity-WebGL

问题&#xff1a;提示gzip压缩报错解决&#xff1a;关闭打包的地方压缩&#xff0c;如下图问题&#xff1a;窗口未全屏解决&#xff1a;使用百分比画布替换固定尺寸画布 参考&#xff1a;新版Unity打包Webgl端进行屏幕自适应_unity webgl分辨率自适应-CSDN博客问题&#xff1a;…

《微信小程序开发从入门到实战》学习九十六

7.2 基础内容组件 7.2.4 progress组件 progress组件的示例代码如下&#xff1a; <progress percent"20" show-info /> 7.3 表单组件 表单组件是用于收集信息的组件。第三章介绍了许多表单组件&#xff0c;包括form、input、textarea、picker、switch、butt…

算法学习记录:动态规划

前言&#xff1a; 算法学习记录不是算法介绍&#xff0c;本文记录的是从零开始的学习过程&#xff08;见到的例题&#xff0c;代码的理解……&#xff09;&#xff0c;所有内容按学习顺序更新&#xff0c;而且不保证正确&#xff0c;如有错误&#xff0c;请帮助指出。 学习工具…

Redis学习——高级篇③

Redis学习——高级篇③ Redis7高级之缓存双写一致性之更新策略探讨&#xff08;三&#xff09; 1.缓存双写一致性2.数据库和缓存一致性的几种更新策略2.1 可停机的情况2.2 不可停机的情况,四种更新策略&#xff08;推荐最后一种&#xff0c;看场景&#xff09;1.❌先…

【Vue实用功能】Vue实现文档在线预览功能,在线预览PDF、Word等office文件

1、Office Web(微软的开发接口) 优点 没有 Office也可以直接查看Office 文件适用于移动端、PC无需下载文件就可以在浏览器中查看 <iframe src"文档地址" frameborder"0" /> const docUrl 外网可预览的地址 const url encodeURIComponent(docUrl…