C++并发编程中怎样避免死锁

devtools/2024/11/15 5:15:29/

在C++并发编程中,死锁是一个常见的问题,特别是在使用多线程和锁机制时。死锁发生在两个或多个线程互相等待对方释放资源时,导致所有线程都无法继续执行。为了避免死锁,开发人员可以采用一些常用的方法和准则。以下是一些有效的方法和准则,以及它们的示例和有效理由。

1. 避免嵌套锁

原则

避免在一个线程中请求多个锁。如果必须使用多个锁,确保以相同的顺序获取它们。

示例
#include <mutex>std::mutex mtx1, mtx2;void thread1() {std::lock_guard<std::mutex> lock1(mtx1);std::lock_guard<std::mutex> lock2(mtx2);// 操作
}void thread2() {std::lock_guard<std::mutex> lock2(mtx2);std::lock_guard<std::mutex> lock1(mtx1);// 操作
}

有效理由

如果两个线程分别以不同顺序获取锁,可能会导致死锁。通过确保所有线程以相同顺序获取锁,可以避免这种情况。

2. 使用std::lock函数

原则

std::lock函数可以同时锁定多个互斥量,不会引起死锁。

示例
#include <mutex>
#include <thread>std::mutex mtx1, mtx2;void thread1() {std::lock(mtx1, mtx2);std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);// 操作
}void thread2() {std::lock(mtx2, mtx1);std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);// 操作
}

有效理由

std::lock函数使用了一种无死锁的算法(如Twins算法)来锁定多个互斥量,确保不会发生死锁。

3. 使用std::scoped_lock

原则

std::scoped_lock是C++17引入的,用于在RAII风格下锁定多个互斥量,避免死锁。

示例
#include <mutex>
#include <thread>std::mutex mtx1, mtx2;void thread1() {std::scoped_lock lock(mtx1, mtx2);// 操作
}void thread2() {std::scoped_lock lock(mtx2, mtx1);// 操作
}

有效理由

std::scoped_lock自动管理多个互斥量的锁定和解锁,确保不会发生死锁。

4. 锁定超时和死锁检测

原则

在锁定失败时设置超时时间,或者使用死锁检测工具,及时发现和处理死锁。

示例
#include <mutex>
#include <chrono>std::timed_mutex mtx;void thread1() {if (mtx.try_lock_for(std::chrono::seconds(1))) {// 操作mtx.unlock();} else {// 处理超时}
}

有效理由

通过设置锁定超时,可以避免线程无限期等待锁,减少死锁发生的可能性。死锁检测工具可以帮助开发人员及时发现和处理死锁。

5. 最小化锁的作用范围

原则

尽量减少持有锁的时间,仅在必要时持有锁。

示例
#include <mutex>std::mutex mtx;
int shared_data = 0;void update_data() {int temp;{std::lock_guard<std::mutex> lock(mtx);temp = shared_data + 1;}// 计算或其他操作{std::lock_guard<std::mutex> lock(mtx);shared_data = temp;}
}

有效理由

通过最小化锁的作用范围,减少其他线程等待锁的时间,降低死锁的风险。

6. 避免持有锁时调用外部代码

原则

避免在持有锁时调用可能阻塞或请求其他锁的外部代码。

示例
#include <mutex>std::mutex mtx;
int shared_data = 0;void external_function() {// 可能请求其他锁或阻塞
}void update_data() {std::lock_guard<std::mutex> lock(mtx);// 直接操作数据// 避免调用 external_function()
}

有效理由

持有锁时调用外部代码可能会导致死锁或其他并发问题。通过避免这种情况,可以减少死锁的风险。

总结

在C++并发编程中,避免死锁需要采取一些有效的方法和准则。通过避免嵌套锁、使用std::lock函数、std::scoped_lock、设置锁定超时、最小化锁的作用范围以及避免持有锁时调用外部代码,可以显著减少死锁的发生。每种方法的有效理由在于它们通过不同的机制(如锁的顺序、超时、作用范围等)来避免或减少死锁的风险。


http://www.ppmy.cn/devtools/133749.html

相关文章

[C++]学习《DirectX12 3D 游戏开发实战》 第八天 利用 Direct3D 绘制几何体(续)

本章将介绍一些此书后面常会用到的绘图模式。首先讲解与绘图优化相关的内容&#xff0c;此处涉及“帧资源 (frame resource)”等概念。若采用帧资源&#xff0c;我们就得修改程序中的渲染循环&#xff0c;好处&#xff1a;不必在每一帧都刷新命令队列&#xff0c;继而改善 CPU …

七招提升工作效率

本文介绍了可以有效提升开发者工作效率的七个技巧。原文: 7 Productivity Hacks I Stole From a Principal Software Engineer Domenico Loia Unsplash 你有没有过与工作风格近乎神奇的高级或首席软件工程师共同工作的经验&#xff1f; 想象一下&#xff0c;有个人只需轻轻一点…

命令行工具进阶指南

&#x1f680; 命令行工具进阶指南&#xff1a;Git、Shell与效率工具的进阶之路 掌握命令行工具&#xff0c;让你的开发效率突飞猛进。本文将深入探讨 Git 高级技巧、Shell 脚本自动化以及各种效率倍增的 CLI 工具。 &#x1f4d1; 目录 Git 高级技巧与工作流Shell 脚本自动化…

SpringBoot(十一)SpringBoot上传文件

今天我们需要做一个文件上传&#xff0c;为我们后边要做的webscoket即时聊天做准备。 一&#xff1a;在配置文件中配置文件上传参数 spring:profiles:# 对&#xff0c;你没看错&#xff0c;环境对应的名称就是子环境-后边的名字&#xff0c;我这里分别对应 dev|productactive:…

容器docker的ulimit

Ulimit 在linux里ulimit命令可以对shell生成的进程的资源进行限制。 常用的ulimit限制 打开文件句柄数core文件大小设置进程能够消耗的虚拟内存设置用户能够打开的进程数目 不太常用的ulimit限制 设置数据段的最大值.单位:kbytes 设置创建文件的最大值.单位:blocks 设置在…

thinkphp自定义命令行+宝塔面板Shell脚本实现定时任务

参考 ThinkPHP宝塔运营环境实现定时任务_宝塔linux面板 thinkphp 定时任务怎么配置-CSDN博客 thinkphp自定义命令行宝塔Shell脚本实现定时任务_宝塔定时执行shell脚本命令-CSDN博客 未实操&#xff0c;如果实操结合上面两个案例

【计算机网络】UDP网络程序

一、服务端 1.udpServer.hpp 此文件负责实现一个udp服务器 #pragma once#include <iostream> #include <string> #include <cstdlib> #include <cstring> #include <functional> #include <strings.h> #include <unistd.h> #incl…

macOS 应用公证指南:使用 fastlane 实现自动化公证流程

背景介绍 在 macOS 系统上,为了保护用户安全,Apple 要求开发者对未通过 Mac App Store 分发的应用程序进行公证(Notarization)。如果应用程序没有经过公证,用户在运行时会看到警告弹窗,这会影响用户体验。虽然开启沙箱模式的应用可以直接通过 App Store 分发来避免这个问题…