C++编程:警惕线程优先级反转

news2024/11/8 5:22:44

文章目录

    • 0. 引言
    • 1. 什么是线程优先级反转?
    • 2. 不正确的示例代码
      • 关键点总结
      • 运行时分析
    • 3. 优先级反转的影响
      • 系统延迟和性能问题:
      • 崩溃或死锁:
    • 4. 如何避免线程优先级反转?
      • 4.1. 分离关键任务和非关键任务
        • 4.2. 高优先级采用非阻塞方式获取锁
      • 4.3. 优先级继承机制(Priority Inheritance)

0. 引言

在多线程编程中,线程之间的资源同步如果处理不当,会出现**线程优先级反转(Priority Inversion)**的情况,这可能导致系统性能下降,甚至引发崩溃。

本文将通过 C++ 示例代码解释优先级反转的概念,并提出避免这一问题的方法。

1. 什么是线程优先级反转?

线程优先级反转通常发生在以下场景中:

  • 高优先级线程(Task A)需要访问一个资源,而这个资源当前被低优先级线程(Task C)占用。
  • 中优先级线程(Task B)在高优先级线程与低优先级线程之间运行,并且它的优先级高于低优先级线程但低于高优先级线程。
  • 当高优先级线程(Task A)请求访问该资源时,由于该资源被低优先级线程(Task C)持有,高优先级线程会被阻塞。
  • 这时,中优先级线程(Task B)会抢占 CPU 资源,并先于高优先级线程执行。更糟糕的是,低优先级线程(Task C)可能由于中优先级线程(Task B)的干扰,无法及时释放资源。

最终,系统可能表现为高优先级线程被延迟执行,低优先级线程却阻塞了高优先级线程的执行,导致性能不稳定或系统崩溃。

2. 不正确的示例代码

如下通过将 lowPriorityTask 中的锁放入 std::lock_guard 里并确保它在资源占用期间持有资源锁,模拟了一个真实的优先级反转问题:低优先级线程占用资源后,高优先级线程被迫等待,直到低优先级线程释放资源。

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

// 定义一个全局资源锁
std::mutex resourceMutex;

// 低优先级线程,模拟长时间占用资源
void lowPriorityTask() {
    std::lock_guard<std::mutex> lock(resourceMutex);  // 低优先级线程持有资源锁
    std::cout << "Low priority task started, holding resource...\n";
    std::this_thread::sleep_for(std::chrono::seconds(3));  // 模拟资源占用
    std::cout << "Low priority task finished, releasing resource.\n";
}

// 中优先级线程,模拟执行某些任务
void mediumPriorityTask() {
    std::cout << "Medium priority task started.\n";
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟任务
    std::cout << "Medium priority task finished.\n";
}

// 高优先级线程,模拟尝试获取资源
void highPriorityTask() {
    std::cout << "High priority task trying to acquire resource...\n";
    std::lock_guard<std::mutex> lock(resourceMutex);  // 需要资源锁
    std::cout << "High priority task acquired resource and started working.\n";
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟任务
    std::cout << "High priority task finished.\n";
}

int main() {
    // 创建低优先级线程,中优先级线程和高优先级线程
    std::thread lowTask(lowPriorityTask);  // 低优先级线程
    std::this_thread::sleep_for(std::chrono::milliseconds(500));  // 等待低优先级线程获取资源

    std::thread mediumTask(mediumPriorityTask);  // 中优先级线程
    std::thread highTask(highPriorityTask);  // 高优先级线程

    // 等待线程完成
    lowTask.join();
    mediumTask.join();
    highTask.join();

    return 0;
}

关键点总结

  1. 低优先级线程 (lowPriorityTask):在执行时获取 resourceMutex 锁,并模拟资源占用 3 秒。

  2. 高优先级线程 (highPriorityTask):尝试在 lowPriorityTask 持有资源时获取锁,由于低优先级线程已经占用资源,高优先级线程将会被阻塞,直到低优先级线程释放锁。

  3. 中优先级线程 (mediumPriorityTask):在低优先级线程占用资源时执行。由于中优先级线程的优先级比低优先级线程高,它会在低优先级线程执行时被调度执行,并不会阻塞。

运行时分析

  1. 低优先级线程 开始执行并锁定资源。
  2. 中优先级线程 开始执行,并占用 CPU,尽管它的优先级低于高优先级线程,但它会先于高优先级线程执行。
  3. 高优先级线程 尝试获得资源并被阻塞,直到低优先级线程释放锁。

输出如下:

Low priority task started, holding resource...
Medium priority task started.
Medium priority task finished.
High priority task trying to acquire resource...
Low priority task finished, releasing resource.
High priority task acquired resource and started working.
High priority task finished.

3. 优先级反转的影响

系统延迟和性能问题:

当优先级反转发生时,系统中的高优先级任务被延迟,可能导致系统响应变慢,影响实时性要求较高的应用,如嵌入式系统、实时操作系统等。

崩溃或死锁:

在某些情况下,优先级反转可能引发更严重的问题。高优先级线程的延迟可能导致系统错过了某些关键的时机,甚至在多线程程序中引发死锁或崩溃。

4. 如何避免线程优先级反转?

4.1. 分离关键任务和非关键任务

原理:

将系统的关键任务和非关键任务分离,避免它们之间的资源竞争,从而减少优先级反转的机会。

实现方法:

  • 使用进程间通信(IPC): 将关键任务和非关键任务放在不同的进程中,通过消息队列、管道等方式通信。
  • 资源隔离: 为不同优先级的任务分配独立的资源,避免共享。
4.2. 高优先级采用非阻塞方式获取锁

原理:

使用非阻塞方式获取锁,避免线程因为等待资源而被长时间阻塞。

实现方法:

  • 尝试获取锁失败时立即返回:

    if (pthread_mutex_trylock(&mutex) == 0) {
        // 获取锁成功
        // 访问共享资源
        pthread_mutex_unlock(&mutex);
    } else {
        // 获取锁失败,执行其他操作
    }
    
  • 设置锁的超时时间:

    struct timespec timeout;
    clock_gettime(CLOCK_REALTIME, &timeout);
    timeout.tv_sec += 1;  // 设置1秒超时
    
    if (pthread_mutex_timedlock(&mutex, &timeout) == 0) {
        // 获取锁成功
        // 访问共享资源
        pthread_mutex_unlock(&mutex);
    } else {
        // 获取锁超时,执行其他操作
    }
    

为了避免线程优先级反转,

4.3. 优先级继承机制(Priority Inheritance)

原理:

优先级继承机制是在低优先级线程占有共享资源而高优先级线程等待该资源时,临时将低优先级线程的优先级提升到与等待线程相同的级别。这样,低优先级线程可以更快地完成任务并释放资源,减少高优先级线程的等待时间。一旦低优先级线程释放了资源,它的优先级就会恢复到原来的水平。

注意,并不是所有的Linux内核都支持该优先级继承机制。一般而言,使用内核PREEMPT-RT是支持优先级继承的。其他内核版本也可能支持,需要确保内核配置中启用了相应的选项(如CONFIG_RT_MUTEXES)。

改进后代码:

#include <pthread.h>
#include <iostream>

pthread_mutex_t mutex;
pthread_mutexattr_t attr;

void initMutexWithPriorityInheritance() {
    if (pthread_mutexattr_init(&attr) != 0) {
        std::cerr << "Failed to initialize mutex attributes\n";
        exit(EXIT_FAILURE);
    }
    if (pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT) != 0) {
        std::cerr << "Failed to set priority inheritance protocol\n";
        pthread_mutexattr_destroy(&attr);
        exit(EXIT_FAILURE);
    }
    if (pthread_mutex_init(&mutex, &attr) != 0) {
        std::cerr << "Failed to initialize mutex\n";
        pthread_mutexattr_destroy(&attr);
        exit(EXIT_FAILURE);
    }
    pthread_mutexattr_destroy(&attr);  // 清理属性对象
}

void lowPriorityTask() {
    if (pthread_mutex_lock(&mutex) != 0) {
        std::cerr << "Failed to lock mutex in low priority task\n";
        return;
    }
    std::cout << "Low priority task: Resource acquired, performing task...\n";
    std::this_thread::sleep_for(std::chrono::seconds(3));  // 模拟任务
    if (pthread_mutex_unlock(&mutex) != 0) {
        std::cerr << "Failed to unlock mutex in low priority task\n";
    }
    std::cout << "Low priority task: Resource released\n";
}

void highPriorityTask() {
    if (pthread_mutex_lock(&mutex) != 0) {
        std::cerr << "Failed to lock mutex in high priority task\n";
        return;
    }
    std::cout << "High priority task: Resource acquired, performing task...\n";
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟任务
    if (pthread_mutex_unlock(&mutex) != 0) {
        std::cerr << "Failed to unlock mutex in high priority task\n";
    }
    std::cout << "High priority task: Resource released\n";
}

int main() {
    initMutexWithPriorityInheritance();

    std::thread lowTask(lowPriorityTask);  // 低优先级线程
    std::this_thread::sleep_for(std::chrono::milliseconds(500));  // 等待低优先级线程获取资源

    std::thread highTask(highPriorityTask);  // 高优先级线程

    // 等待线程完成
    lowTask.join();
    highTask.join();

    pthread_mutex_destroy(&mutex);  // 清理互斥锁

    return 0;
}

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

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

相关文章

【数学】通用三阶矩阵特征向量的快速求法 超简单!!!

目录 三个定理1、3个特征值&#xff08;即根互不相等&#xff09;例题实践2、2个特征值&#xff08;即有一个双重根&#xff09;3、1个特征值&#xff08;即有一个三重根&#xff09;定理证明 三个定理 本定理适用于 所有三阶矩阵 的特征向量求法&#xff01; 1、3个特征值&…

MapReduce 的 Shuffle 过程

MapReduce 的 Shuffle 过程指的是 MapTask 的后半程&#xff0c;以及ReduceTask的前半程&#xff0c;共同组成的。 从 MapTask 中的 map 方法结束&#xff0c;到 ReduceTask 中的 reduce 方法开始&#xff0c;这个中间的部分就是Shuffle。是MapReduce的核心&#xff0c;心脏。 …

【WebRTC】视频采集模块中各个类的简单分析

目录 1.视频采集模块中的类1.1 视频采集基础模块&#xff08;VideoCaptureModule&#xff09;1.2 视频采集工厂类&#xff08;VideoCaptureFactory&#xff09;1.3 设备信息的实现&#xff08;DeviceInfoImpl&#xff09;1.4 视频采集的实现&#xff08;VideoCaptureImpl&#…

江协科技STM32学习- P40 硬件SPI读写W25Q64

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

智慧场馆:安全、节能与智能化管理的未来

在当今社会&#xff0c;智慧场馆已经成为了现代场馆建设的一种重要模式。通过整合先进技术和智能系统&#xff0c;智慧场馆致力于提供全方位的解决方案&#xff0c;以实现场馆的安全性、节能性和智能化管理。本文将深入探讨智慧场馆如何实现安全、节能和全面智能化&#xff0c;…

Facebook与人工智能:推动社交媒体发展的新动力

在数字化时代的浪潮中&#xff0c;社交媒体已成为人们日常生活不可或缺的一部分。作为全球最大的社交平台之一&#xff0c;Facebook凭借其庞大的用户基础和先进的技术&#xff0c;正积极探索与人工智能&#xff08;AI&#xff09;的结合&#xff0c;以推动社交媒体的不断发展。…

【论文复现】自动化细胞核分割与特征分析

本文所涉及所有资源均在这里可获取。 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 论文复现 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 自动化细胞核分割与特征分析 引言效果展示HoverNet概述HoverNet原理分析整…

阿里云文本内容安全处理

1、什么是内容安全 内容安全是一款基于AI算法和云计算技术&#xff0c;对多媒体内容的不宜或违规内容提供识别和标注的产品。该产品&#xff0c;支持对各行业及业务场景下的图片、视频、文本、语音等对象进行检测&#xff0c;可以帮助您提高内容审核效率、提高平台内容质量和用…

双指针算法习题解答

1.移动零 题目链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 题目解析&#xff1a;该题要求将数组中为0的元素全部转移到数组的末尾&#xff0c;同时不能改变非零元素的相对位置。 解题思路&#xff1a;我们可以用变量dest和cur将该数组分为三个区域。…

idea、pycharm等软件的文件名红色怎么变绿色

1.问题 有时候在写完代码打算提交的时候&#xff0c;会遇到某个资源文件不是绿色的&#xff0c;不能提交 2.解决方法 右键该文件——git——添加即可 3.不同颜色含义 3.1 蓝色&#xff08;Blue&#xff09; 含义&#xff1a;文件被修改了但尚未保存。蓝色通常表示文件自上…

Python进阶之IO操作

文章目录 一、文件的读取二、文件内容的写入三、之操作文件夹四、StringIO与BytesIO 一、文件的读取 在python里面&#xff0c;可以使用open函数来打开文件&#xff0c;具体语法如下&#xff1a; open(filename, mode)filename&#xff1a;文件名&#xff0c;一般包括该文件所…

ECharts折线图背景渐变设置

目录 引入 1.在一个HTML文件中编写两个图表 2.渐变背景 引入 如何在一个HTML文件中编写两个图表&#xff1a;&#xff08;这个例子基于这个篇文章的基础&#xff09;一篇搞懂前端获取数据-CSDN博客 一个例子&#xff1a; 1.在一个HTML文件中编写两个图表 重点在于名字的不重…

基于SpringBoot的“乐校园二手书交易管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“乐校园二手书交易管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页界面图 用户注册界面图 二手…

CSS弹性布局:灵活布局的终极指南

在网页设计中&#xff0c;CSS 弹性布局&#xff08;Flexbox&#xff09;是一个不可或缺的工具。它能帮助你轻松地排列和对齐元素&#xff0c;尤其是在响应式设计中表现出色。今天&#xff0c;我们就来深入探讨一下 Flexbox 的各个属性&#xff0c;让你彻底掌握这个强大的布局工…

OpenJDK Vendor下载选择

首先JDK可以通过idea进行安装 File➡️Project Structure➡️SDK➡️Add SDK➡️Download JDK 然后在JDK版本选择时&#xff0c;Idea提供了很多版本&#xff0c;让我茫然了 OpenJDK国外厂商 供应商 说明 Amazon Corretto 亚马逊云基于OpenJDK构建&#xff0c;收费 Eclipse…

SAP-ABAP开发-ONLINE 程序、DIALOG屏幕开发

目录 一、Online 程序概览 1、程序类型 2、Online程序的主要对象 二、界面 1、SAP的屏幕开发 2、屏幕功能实现 3、界面中的事件块&#xff08;Event Block&#xff09; 4、界面的创建 三、简单界面元素 1、文本/输入框控件 2、数据检查 3、一些常用的关键字 四、复…

基于vue框架的的留守儿童帮扶系统143b5(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;留守儿童,帮扶活动,申请记录,帮扶机构,帮扶进度,帮扶人,申请加入记录,参与帮扶记录 开题报告内容 基于Vue框架的留守儿童帮扶系统开题报告 一、研究背景与意义 随着城乡经济差异的不断扩大&#xff0c;大量农村劳动力涌向城市寻求更好…

xftp连接中不成功 + sudo vim 修改sshd_config不成功的解决方法

我们使用sudo vim不成功&#xff0c;但是我们使用sudo su就可以 了&#xff01; root用户权利更大&#xff01; 喵的&#xff0c;终于成功了&#xff0c;一个xftp连接半天不成功。&#xff08;添加上面的内容就可以连接成功了↑&#xff09;

这款Chrome 插件,使浏览器页面快速滑动到最底部和最顶部,并且还能...

前言 前几日我在使用谷歌浏览器&#xff0c;也就是chrome的时候&#xff0c;浏览一个内容很长的页面&#xff0c;由于页面上的内容有前后关联&#xff0c;所以我必须不停地切换到上面和下面。这非常不方便。使我非常抓狂。后来&#xff0c;我灵机一动&#xff0c;去谷歌浏览器…

嵌入式linux中设备树控制硬件的方法

大家好,今天主要给大家分享一下,如何使用linux系统下的设备树进行硬件控制方法。 第一:linux系统中设备树驱动LED原理 在linux系统中可以使用设备树向Linux内核传递相关的寄存器地址,linux驱动中使用OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关…