当前位置: 首页 > article >正文

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

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

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.kler.cn/a/505598.html

相关文章:

  • citrix netscaler13.1 重写负载均衡响应头(基础版)
  • 【2024年华为OD机试】 (C卷,100分)- 用连续自然数之和来表达整数(Java JS PythonC/C++)
  • word-break控制的几种容器换行行为详解
  • springboot程序快速入门
  • ImageSharp图形库学习
  • 浅谈云计算07 | 云安全机制
  • 基于ADMM交替方向乘子法的超大规模储备系统分布式协同优化算法收敛性matlab仿真与分析
  • PostgreSQL 的一些常用命令
  • LabVIEW与WPS文件格式的兼容性
  • 如何搭建 Vue.js 开源项目的 CI/CD 流水线
  • 《大语言模型后训练技术:指令、偏好、强化微调的深度解析与发展趋势》
  • 计算机网络 (42)远程终端协议TELNET
  • Vue2+OpenLayers给标点Feature添加信息窗体(提供Gitee源码)
  • 基于django中医药数据可视化平台(源码+lw+部署文档+讲解),源码可白嫖!
  • LeetCode热题100-二叉树的中序遍历【JavaScript讲解】
  • 11-1.Android 项目结构 - androidTest 包与 test 包(单元测试与仪器化测试)
  • 【C】数组和指针的关系
  • Ubuntu 安装和配置 MariaDB
  • 【行空板K10】上传温湿度信息到EasyIoT平台
  • redis闪退打不开Creating Server TCP listening socket *:6379: listen: Unknown error
  • ESP8266固件烧录
  • 利用Python爬虫按图搜索1688商品(拍立淘)的探索之旅
  • 从CRUD到高级功能:EF Core在.NET Core中全面应用(二)
  • 鸿蒙报错Init keystore failed: keystore password was incorrect
  • 【element plus】虚拟dom表格中cellRenderer如何使用v-for循环渲染item
  • 【vue3】 defineExpose 的使用