C++并发编程之异常安全性增强

news/2025/1/17 21:17:54/

并发编程中,异常安全是一个非常重要的方面,因为并发环境下的错误处理比单线程环境更加复杂。当多个线程同时执行时,异常不仅可能影响当前线程,还可能影响其他线程和整个程序的稳定性。以下是一些增强并发程序异常安全性的方法,并附有示例代码。

1. 异常捕获和处理

在多线程程序中,每个线程都应该有自己的异常捕获机制。常见的做法是在每个线程的入口点(如线程函数)中使用 try-catch 块来捕获和处理异常。

示例代码:
#include <iostream>
#include <thread>
#include <exception>void threadFunction() {try {// 模拟可能抛出异常的代码throw std::runtime_error("An error occurred in the thread");} catch (const std::exception& e) {std::cerr << "Exception caught in thread: " << e.what() << std::endl;// 可以在这里进行日志记录、资源清理等操作}
}int main() {std::thread t(threadFunction);t.join();return 0;
}

2. 资源管理

使用 RAII(Resource Acquisition Is Initialization)技术来管理资源,确保资源在异常情况下也能正确释放。C++ 中的智能指针(如 std::unique_ptr 和 std::shared_ptr)和 std::lock_guard 等都是 RAII 的典型应用。

示例代码:
#include <iostream>
#include <thread>
#include <memory>
#include <mutex>std::mutex mtx;void threadFunction() {try {std::unique_ptr<int> resource(new int(42));std::lock_guard<std::mutex> lock(mtx);// 模拟可能抛出异常的代码throw std::runtime_error("An error occurred in the thread");} catch (const std::exception& e) {std::cerr << "Exception caught in thread: " << e.what() << std::endl;}
}int main() {std::thread t(threadFunction);t.join();return 0;
}

3. 线程同步

在多线程环境中,确保线程间的同步非常重要。使用互斥锁、条件变量等同步原语时,要确保在异常情况下不会导致死锁或资源泄露。

示例代码:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void prepare() {try {std::this_thread::sleep_for(std::chrono::milliseconds(1000));std::lock_guard<std::mutex> lock(mtx);ready = true;cv.notify_one();} catch (const std::exception& e) {std::cerr << "Exception caught in prepare: " << e.what() << std::endl;// 可以在这里进行日志记录、资源清理等操作}
}void waitAndPrint() {try {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [] { return ready; });std::cout << "Ready is true" << std::endl;} catch (const std::exception& e) {std::cerr << "Exception caught in waitAndPrint: " << e.what() << std::endl;}
}int main() {std::thread t1(prepare);std::thread t2(waitAndPrint);t1.join();t2.join();return 0;
}

4. 异常传播

在多线程环境中,异常可能需要从一个线程传播到另一个线程。可以使用 std::promise 和 std::future 来实现异常的跨线程传播。

示例代码:
#include <iostream>
#include <thread>
#include <future>void threadFunction(std::promise<int> promise) {try {// 模拟可能抛出异常的代码throw std::runtime_error("An error occurred in the thread");promise.set_value(42);} catch (const std::exception& e) {promise.set_exception(std::current_exception());}
}int main() {std::promise<int> promise;std::future<int> future = promise.get_future();std::thread t(threadFunction, std::move(promise));t.join();try {int value = future.get();std::cout << "Value: " << value << std::endl;} catch (const std::exception& e) {std::cerr << "Exception caught in main: " << e.what() << std::endl;}return 0;
}

5. 日志记录

在多线程程序中,记录详细的日志是诊断问题的重要手段。可以使用日志库(如 spdlog)来记录日志信息。

示例代码:
#include <iostream>
#include <thread>
#include <spdlog/spdlog.h>void threadFunction() {try {// 模拟可能抛出异常的代码throw std::runtime_error("An error occurred in the thread");} catch (const std::exception& e) {spdlog::error("Exception caught in thread: {}", e.what());// 进行其他必要的处理}
}int main() {auto logger = spdlog::stdout_color_mt("console");std::thread t(threadFunction);t.join();return 0;
}

6. 使用线程池

线程池可以更好地管理和复用线程,减少线程创建和销毁的开销。线程池通常会处理线程中的异常,并确保线程池的正常运行。

示例代码:
#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>class ThreadPool {
public:ThreadPool(size_t numThreads) : stop(false) {for (size_t i = 0; i < numThreads; ++i) {threads.emplace_back([this] {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(queueMutex);condition.wait(lock, [this] { return stop || !tasks.empty(); });if (stop && tasks.empty()) {return;}task = std::move(tasks.front());tasks.pop();}try {task();} catch (const std::exception& e) {std::cerr << "Exception caught in thread pool: " << e.what() << std::endl;}}});}}~ThreadPool() {{std::unique_lock<std::mutex> lock(queueMutex);stop = true;}condition.notify_all();for (std::thread& t : threads) {t.join();}}template <typename Func, typename... Args>auto enqueue(Func&& func, Args&&... args) -> std::future<decltype(func(args...))> {using return_type = decltype(func(args...));auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<Func>(func), std::forward<Args>(args)...));std::future<return_type> res = task->get_future();{std::unique_lock<std::mutex> lock(queueMutex);if (stop) {throw std::runtime_error("Enqueue on stopped ThreadPool");}tasks.emplace([task]() { (*task)(); });}condition.notify_one();return res;}private:std::vector<std::thread> threads;std::queue<std::function<void()>> tasks;std::mutex queueMutex;std::condition_variable condition;bool stop;
};void simulateWork() {throw std::runtime_error("An error occurred in the task");
}int main() {ThreadPool pool(4);std::future<void> future = pool.enqueue(simulateWork);try {future.get();} catch (const std::exception& e) {std::cerr << "Exception caught in main: " << e.what() << std::endl;}return 0;
}

总结

并发编程中,确保异常安全需要从多个方面着手,包括异常捕获和处理、资源管理、线程同步、异常传播、日志记录和使用线程池等。通过这些方法,可以有效地处理并发环境中的异常,提高程序的稳定性和可靠性。


http://www.ppmy.cn/news/1563972.html

相关文章

redis性能优化参考——筑梦之路

基准性能测试 redis响应延迟耗时多长判定为慢&#xff1f; 比如机器硬件配置比较差&#xff0c;响应延迟10毫秒&#xff0c;就认为是慢&#xff0c;机器硬件配置比较高&#xff0c;响应延迟0.5毫秒&#xff0c;就认为是慢。这个没有固定的标准&#xff0c;只有了解了你的 Red…

DATACOM-防火墙-复习-实验

防火墙 概述配置实验参考 概述 与路由器对比 路由器防火墙功能寻址、流量转发、路由流量控制、安全区域隔离防护硬件-相较于路由器&#xff0c;多了SPU&#xff0c;用于DDoS攻击防范/匹配会话/状态检测/认证策略/安全策略/NAT 策略/内容安全带宽策略等 发展 安全区域 防火墙…

PlantUML流程图语法学习实践

前言 PlantUML流程图在软件开发中具有多种用途&#xff0c;使用PlantUML生成的UML图表可以帮助审查者更快地理解代码的结构和逻辑&#xff1b;在AI发展的大环境中&#xff0c;可以借助plantUML语法生成的伪代码形式利用promt直接生成开发代码。 一、PlantUML PlantUML 是一款非…

GitLab CI/CD使用runner实现自动化部署前端Vue2 后端.Net 7 Zr.Admin项目

1、查看gitlab版本 建议安装的runner版本和gitlab保持一致 2、查找runner 执行 yum list gitlab-runner --showduplicates | sort -r 找到符合gitlab版本的runner&#xff0c;我这里选择 14.9.1版本 如果执行出现找不到下载源&#xff0c;添加官方仓库 执行 curl -L &quo…

数据结构-顺序表及链表结构分析

文章目录 一、顺序表的插入&删除Ⅰ.顺序表的插入(增)1.1 顺序表的尾插1.2 顺序表的头插1.3 顺序表的指定位置插入 Ⅱ.顺序表的移除元素(删)2.1 顺序表的尾删2.2 顺序表的头删2.3 顺序表的指定位置删除 Ⅲ.顺序表数据的查找Ⅳ.顺序表的销毁 二、链表的插入(增)1.单链表的尾插…

HTTP:Nagle算法与TCP_NODELAY

背景&#xff1a;TCP 有一个数据流接口&#xff0c;应用程序可以通过它将任意尺寸的数据放入 TCP 栈中—— 即使一次只放一个字节也可以!但是&#xff0c;每个 TCP 段中都至少装载了 40 个字节的标记和首部&#xff0c;所以如果 TCP 发送了大量包含少量数据的分组&#xff0c;网…

【 MySQL学习1】简介

文章目录 一、一些概念1.1 什么是数据库1.2 数据库管理系统1.3 SQL1.4 三者之间的关系 二 安装MySQL数据库三、命令启动3.1 停止服务3.2 启动服务 一、一些概念 1.1 什么是数据库 英文单词DataBase&#xff0c; 简称DB。 按照一定格式存储数据的一些文件的组合。 存储数据的仓…

微信小程序:实现首页权限菜单效果

一、效果 二、数据库表 1、菜单表 包含权限名称,模块名称,图标名称,页面跳转的方法,菜单是否显示栏位 2、角色对应权限表 包含角色id和权限id,这个表将角色和菜单角色连接,给角色赋予权限功能 3、 账户表 用于绑定账号隶属于什么角色,绑定的角色表 4、角色表 菜单的…