_Linux多线程-死锁Linux线程同步篇

news2024/12/29 10:15:01

文章目录

  • 1. 死锁
    • 死锁四个必要条件
    • 避免死锁
    • 避免死锁算法(了解)
  • 2. Linux线程同步
    • 线程同步出现的背景
    • 条件变量
    • 同步概念与竞态条件
    • 条件变量函数
      • 1. 初始化
      • 2. 销毁
      • 3. 等待条件满足
      • 4. 唤醒等待
    • 小结
    • 测试实验

1. 死锁

  • 死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。
    在这里插入图片描述

死锁四个必要条件

  • 互斥条件:一个资源每次只能被一个执行流使用。
  • 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系。

就好比两个小朋友都只有5毛;两个小朋友都想吃1块钱的棒棒糖;两位小朋友在商量的过程中,都拿不到对方的五毛钱更不用说吃棒棒糖了。

避免死锁

  • 破坏死锁的四个必要条件
  • 加锁顺序一致
  • 避免锁未释放的场景
  • 资源一次性分配

避免死锁算法(了解)

  • 死锁检测算法(了解)
  • 银行家算法(了解)

2. Linux线程同步

线程同步出现的背景

肯定是可以避免死锁的情况出现

    1. 频繁的申请到资源和对方的资源了(别人怎么办)—即可能造成别人的饥饿的问题
      • 例如:学校食堂打饭,前面总有人插队,你就可能吃不上饭(就好比一个轻量级进程的优先级很高一样)。
    1. 有一个线程再访问临界资源;你这个线程光过来询问就太过于浪费时间了(可以引入条件变量)
      • 例如:有一次你去买买件商品,商品没货;你就 天天来商店去询问售货员。这种解决方法太浪费时间了,还不如加个微信,有货的时候让售货员通知你就可以了。

条件变量

  • 当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。
    • 例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。

同步概念与竞态条件

  • 同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步。
  • 竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。

条件变量函数

1. 初始化

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
参数:
cond:要初始化的条件变量
attr:NULL

2. 销毁

int pthread_cond_destroy(pthread_cond_t *cond)

3. 等待条件满足

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量(线程互斥篇里的锁)

4. 唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);
全部线程一起唤醒
int pthread_cond_signal(pthread_cond_t *cond);
线程一个一个按顺序来唤醒

小结

  • 引入同步: 主要是为了解决,访问临界资源合理性问题的

    • 按照一定的顺序,进行临界资源的访问—线程同步
  • 条件变量

  • 当我们申请临界资源前-> 先要做临界资源是否存在的检测->要做检测的本质也是访问临界资源!

    • 结论: 对临界资源的检测,也一定是需要在加锁和解锁之间的!
    • 常规方式要检测条件就绪 ,注定了我们必须频繁申请和释放锁(需要条件变量)
  • 条件变量

    • 1不要让线程在频繁自己检测啦—等待
    • 2.当条件就绪的时候,通知对应的线程,让他来进行资源申请和访问!

测试实验

  • 实验代码块:
#include <iostream>
#include <unistd.h>
#include <thread>

using namespace std;

#define THREAD_NUM 4                   // 线程个数
typedef void (*funcs)(const std::string &name,pthread_mutex_t *pmtx, pthread_cond_t *pcond); // 函数指针
// pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

volatile bool quit = false;            // 退出条件变量

class threadData
{
public:
    threadData(const string &name, funcs func, pthread_mutex_t *mtx, pthread_cond_t *cond)
        : _name(name), _func(func), _mtx(mtx), _cond(cond)
    {
    } // 类初始化
public:
    string _name; // 线程名字
    funcs _func;
    pthread_mutex_t *_mtx;
    pthread_cond_t *_cond;
};

void Func1(const std::string &name,pthread_mutex_t *pmtx, pthread_cond_t *pcond)
{
    while (!quit)
    {
        pthread_mutex_lock(pmtx);           // 申请锁
        pthread_cond_wait(pcond, pmtx); // 默认该线程在执行的时候,wait代码被执行,当前线程会被立即被阻塞
        cout << name << "...我正在执行数据扫描任务..." << endl;
        pthread_mutex_unlock(pmtx); // 解锁
    }
}
void Func2(const std::string &name,pthread_mutex_t *pmtx, pthread_cond_t *pcond)
{
    while (!quit)
    {
        pthread_mutex_lock(pmtx);           // 申请锁
        pthread_cond_wait(pcond, pmtx); // 默认该线程在执行的时候,wait代码被执行,当前线程会被立即被阻塞
        cout << name << "...我正在执行下载任务..." << endl;
        pthread_mutex_unlock(pmtx); // 解锁
    }
}
void Func3(const std::string &name,pthread_mutex_t *pmtx, pthread_cond_t *pcond)
{
    while (!quit)
    {
        pthread_mutex_lock(pmtx);           // 申请锁
        pthread_cond_wait(pcond, pmtx); // 默认该线程在执行的时候,wait代码被执行,当前线程会被立即被阻塞
        cout << name << "...我正在执行数据刷新任务..." << endl;
        pthread_mutex_unlock(pmtx); // 解锁
    }
}
void Func4(const std::string &name,pthread_mutex_t *pmtx, pthread_cond_t *pcond)
{
    while (!quit)
    {
        pthread_mutex_lock(pmtx);           // 申请锁
        pthread_cond_wait(pcond, pmtx); // 默认该线程在执行的时候,wait代码被执行,当前线程会被立即被阻塞
        cout << name << "...我正在执行广播任务..." << endl;
        pthread_mutex_unlock(pmtx); // 解锁
    }
}
void *Entry(void *args)
{
    threadData *td = (threadData *)args;
    td->_func(td->_name, td->_mtx, td->_cond); // 函数回调
    delete td;

    return nullptr;
}

int main()
{
    pthread_mutex_t mtx;
    pthread_cond_t cond;
    // mtx && cond 初始化
    pthread_mutex_init(&mtx, nullptr);
    pthread_cond_init(&cond, nullptr);

    pthread_t tids[THREAD_NUM]; // 线程id
    funcs func_t[THREAD_NUM] = {Func1, Func2, Func3, Func4};
    for (int i = 0; i < THREAD_NUM; ++i)
    {
        string name = "thread ";
        name += to_string(i + 1);
        threadData *td = new threadData(name, func_t[i], &mtx, &cond);
        // 创建线程
        pthread_create(tids + i, nullptr, Entry, td);
    }

    sleep(5);
    // cond---唤醒
    int cnt = 10;
    while (cnt)
    {
        cout << "线程正在唤醒 " << cnt-- << endl;
        pthread_cond_signal(&cond);
        sleep(1);
        cout << "唤醒成功" << endl;
    }

    quit = true;
    pthread_cond_broadcast(&cond);  //这一次,让其它线程出来(剩下的线程全部唤醒)
    for (int i = 0; i < THREAD_NUM; ++i)
    {
        // 线程等待
        pthread_join(tids[i], nullptr);
        cout << "thread " << tids[i] << "...quit" << endl;
    }
    cout << THREAD_NUM << "个线程任务结束" << endl;

    //  mtx && cond销毁
    pthread_mutex_destroy(&mtx);
    pthread_cond_destroy(&cond);

    return 0;
}
  • 实验示意图:
    在这里插入图片描述

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

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

相关文章

linux服务器CPU飙高排查

文章目录前言一、第一步 top二、根据pid查找具体线程2.根据pid找到16进制3. 根据进程和线程查找原因总结前言 系统cpu飙高,尤其对于后端人员来说,其实应该学会排查,这样也算是综合能力的体现;那么当出现了cpu严重飙高的时候怎么排查呢? 一、第一步 top 直接在问题服务器输入…

如何系统地学习 C++ 语言?

C 最大的缺点就是语法复杂&#xff0c;而且难学难精。 学习 C 语言也许一本 K&R 的《C程序设计语言》就够了&#xff0c;但是在 C 世界里&#xff0c;找不到这样一本书。在这个爱速成的年代&#xff0c;大家可能耐不住寂寞花很长时间去学习一门语言&#xff0c;所以可以看…

车载以太网 - DoIP电源模式 - 07

DoIP电源模式&节点状态&激活检查这3块内容没有太大的逻辑性可言,主要是概念性内容介绍,所以本篇内容可能会略显枯燥,不过我回尽量完整的把这几块内容介绍清晰,为后面的测试用例设计做好铺垫,方便大家在看完文章后,更加快速的提取知识点,并完成测试设计。 诊断电…

回溯法--图的m着色问题--子集树

问题描述 给定无向连通图和m种不同的颜色&#xff0c;用这些颜色为图G的各个顶点着色&#xff0c;每个顶点有一种颜色 是否有一种着色方法&#xff1f;使得图G中每条边的两个顶点有不同的颜色 这个问题就是图的m可着色判定问题 色数&#xff1a;如果有一个图最少需要m种颜色…

C进阶:自定义类型:结构体、枚举、联合体

自定义类型&#xff1a;结构体、枚举、联合体自定义类型&#xff1a;结构体、枚举、联合体结构体结构的定义&#xff1a;结构体的声明&#xff1a;特殊的声明(匿名)结构体的自引用&#xff1a;结构体变量的定义和初始化结构体内存对齐为什么要有内存对齐修改默认对齐数结构体传…

李宏毅ML-自动调整学习速率

自动调整学习速率 文章目录自动调整学习速率1. RMS and Adagrad2. RMSProp and Adam3. Learning rate scheduling3. 总结1. RMS and Adagrad 在下面有两幅图&#xff0c;如第一幅图所示&#xff0c;随着 iteration 的增加&#xff0c;loss 在不断减少&#xff0c;最后趋近于 0…

广告业务系统 之 智能保险丝 —— “智能流控”

文章目录广告业务系统 之 智能保险丝 —— “智能流控”智能流控常规流量调控数据源计算智能流控功能挂载阈值存储架构长短板服务构建及部署广告业务系统 之 智能保险丝 —— “智能流控” 除了 在 AB 环节 设计了出色的 重试机制 —— “ 双发 ” 外&#xff0c;在 ADX 系统的…

【Redis】Redis实现全局唯一ID

【Redis】Redis实现全局唯一ID 为什么要使用Redis实现全局唯一ID去替代传统的数据库自增ID&#xff0c;主要原因如下&#xff1a; 数据库自增ID的规律性太明显受单表数据量的限制&#xff0c;数据量很大时分表会出现ID重复的现象 1. 全局ID生成器 出于以上原因&#xff0c;我…

Vue3——第十三章(插槽 Slots)

一、插槽内容与出口 这里有一个 <FancyButton> 组件&#xff0c;可以像这样使用&#xff1a; 而 <FancyButton> 的模板是这样的&#xff1a; <slot> 元素是一个插槽出口 (slot outlet)&#xff0c;标示了父元素提供的插槽内容 (slot content) 将在哪里被…

excel图表技巧:如何用填充单元格制作比率分析图

在工作中&#xff0c;我们经常要向上级领导汇报某个指标的进度或完成情况。有时候&#xff0c;我们会用仪表盘或温度计图来展示数据。不会这类型图表的朋友&#xff0c;不用担心&#xff0c;因为今天&#xff0c;我将教给大家一种更简单的方法&#xff01;公司新来的职员小明&a…

你是真的“C”——详解C语言函数模块知识(下篇)

详解C语言函数模块知识(下篇&#xff09;&#x1f60e;前言&#x1f64c;1、 函数的嵌套调用和链式访问&#x1f64c;1.1 嵌套调用&#x1f49e;1.2 链式访问&#x1f49e;2、函数的声明和定义&#x1f64c;2.1函数声明&#x1f49e;2.1函数定义&#x1f49e;3、函数递归&#…

【算法题解】9. 邻值查找

文章目录题目解题思路代码实现复杂度分析这是一道中等难度的题。 题目来自&#xff1a;AcWing 题目 给定一个长度为 n 的序列 A&#xff0c;A 中的数各不相同。 对于 A 中的每一个数 Ai&#xff0c;求&#xff1a; min|Ai−Aj|&#xff0c;其中 1 < j < i。 以及令上式…

10行代码带你轻松抓取博客清单

一、前言 今天在网上偶遇一款html解析利器HtmlAgilityPack&#xff0c;免费下载地址&#xff1a;入口。 HtmlAgilityPack是.net下的一个HTML解析类库&#xff0c;支持用XPath来解析HTML。通过该类库&#xff0c;先通过浏览器获取到xpath获取到节点内容然后再通过正则表达式匹…

【operator bool】while(cin >> str)是什么意思?

文章目录一、前言二、cin是什么&#xff1f;三、隐式类型转化如何发生&#xff1f;一、前言 在oj题中&#xff0c;为了实现多行输入&#xff0c;我们经常可以看到这样的写法&#xff1a;while(cin >> str)&#xff0c;这究竟是什么意思呢&#xff1f;为了理解其中的含义&…

c++ 可变参数的三种实现方式

c 可变参数 方法一&#xff1a; C语言的: va_list1 #include <stdio.h> #include <stdarg.h>int add_nums(int count, ...) {int result 0;va_list args;va_start(args, count); // C23 起能省略 countfor (int i 0; i < count; i) {result va_arg(args, i…

bresenham algorithm

#! https://zhuanlan.zhihu.com/p/598780689 bresenham algorithm 全象限区域bresenham algorithm计算的python/c实现 bresenham algorithm为计算机图形学中使用像素点显示直线的算法&#xff0c;算法使用整数运算&#xff0c;能大幅提升计算速度。最近概率栅格建图算法中涉及…

CloudCanal实战-Oracle数据迁移同步到PostgreSQL

简述 本篇文章主要介绍如何使用 CloudCanal 构建一条 Oracle 到 PostgreSQL 的数据同步链路 技术要点 缩小的数据库权限要求 CloudCanal 对 Oracle 数据库的高权限要求&#xff0c;主要来自两个面向 DBA 的操作&#xff0c;自动构建字典和 自动切换归档日志&#xff0c;这两…

详解 strtok 函数以及模拟实现

目录 一、strtok 函数的介绍 二、strtok 函数的模拟实现 一、strtok 函数的介绍 函数原型&#xff1a; char* strtok(char* str, const char* delimiters); delimiter n.[计]分隔符&#xff0c;定界符&#xff08;a character that marks the beginning or end of a unit o…

KMP算法详解+动图演示

目录 一、KMP算法简介 二、KMP算法的详细图解 1. 先了解BF算法的基本思路 2. 简单了解KMP算法 3. next数组的引入 4. next数组的代码实现&#xff08;含动态演示&#xff09; 三、KMP算法完整代码 一、KMP算法简介 KMP算法是一种改进的字符串匹配算法&#xff0c;由 …

【算法】二分图判定

目录1.概述2.代码实现3.应用本文参考&#xff1a; LABULADONG 的算法网站 1.概述 &#xff08;1&#xff09;二分图 (Bipartite Graph)&#xff0c;又称为二部图&#xff0c;是图论中的一种特殊模型。 设 G (V, E) 是一个无向图&#xff0c;如果顶点 V 可分割为两个互不相交的…