欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 旅游 > C++中如何处理线程函数抛出的异常

C++中如何处理线程函数抛出的异常

2025/9/18 1:50:38 来源:https://blog.csdn.net/qqrrjj2011/article/details/143324389  浏览:    关键词:C++中如何处理线程函数抛出的异常

文章目录

    • 两种处理方法
    • 案例展示
    • 案例扩展
      • 说明

两种处理方法

在C++中,如果你在一个通过std::thread创建的线程中抛出了一个异常,并且没有在该线程中捕获它,那么当线程结束时,异常会被传递到包含线程。但是,直接从std::thread对象中获取这个异常并不是直截了当的。

从C++17开始,你可以使用std::thread::join()来等待线程完成,并且如果线程中有未捕获的异常,则会传播到调用join()的代码中。这意味着如果你的线程抛出了一个异常并且你尝试调用join(),那么这个异常会在主线程中被捕获。

以下是一个简单的例子:

#include <iostream>
#include <thread>
#include <exception>void throwExceptionInThread()
{throw std::runtime_error("Exception from thread");
}int main()
{std::thread t(throwExceptionInThread);try {t.join(); // 如果线程中有异常抛出,这里会传播出来} catch (const std::exception& e) {std::cerr << "Caught exception: " << e.what() << '\n';}return 0;
}

在这个例子中,如果throwExceptionInThread函数抛出一个异常,那么这个异常会在main函数中被捕获并处理。

对于C++11和C++14,你也可以这样做,但是如果没有显式地捕获异常,那么这个异常就会丢失,因为std::thread对象一旦销毁,异常信息就会消失。因此,在这些标准版本中,确保你的线程逻辑总是捕获任何可能抛出的异常,或者确保你在std::thread对象销毁之前捕获这些异常。

另外一种方法是手动管理异常,比如通过使用共享状态来传递异常信息:

#include <iostream>
#include <thread>
#include <exception>
#include <shared_ptr>class ExceptionStorage {
public:void setException(std::exception_ptr ep) { m_exception = ep; }std::exception_ptr getException() { return m_exception; }private:std::exception_ptr m_exception;
};void throwExceptionInThread(std::shared_ptr<ExceptionStorage> storage)
{try {// 模拟线程工作throw std::runtime_error("Exception from thread");} catch (...) {// 将异常存储在共享状态中storage->setException(std::current_exception());}
}int main()
{auto storage = std::make_shared<ExceptionStorage>();std::thread t(throwExceptionInThread, storage);t.join(); // 等待线程结束std::exception_ptr ep = storage->getException();if (ep) {try {std::rethrow_exception(ep);} catch (const std::exception& e) {std::cerr << "Caught exception: " << e.what() << '\n';}}return 0;
}

这种方法可以确保即使在线程结束之后,异常仍然可以被捕获和处理。

案例展示

让我们来看一个具体的示例,其中我们将在一个线程中执行一些任务,并处理可能发生的异常。我们将创建一个类来存储异常,并将此类的实例作为参数传递给线程。这样,如果线程中的操作抛出异常,我们可以将异常保存在共享状态中,并在主线程中重新抛出。

假设我们需要在一个独立的线程中执行一些文件处理任务,比如读取文件并将内容转换为大写。为了演示异常处理机制,我们故意让程序在某些条件下抛出异常。

以下是完整的示例代码:

#include <iostream>
#include <thread>
#include <exception>
#include <memory>
#include <fstream>
#include <string>// 用于在线程间传递异常的类
class ExceptionStorage {
public:void setException(std::exception_ptr ep) { m_exception = ep; }std::exception_ptr getException() const { return m_exception; }private:std::exception_ptr m_exception;
};// 在线程中运行的任务
void processFile(const std::string& filename, std::shared_ptr<ExceptionStorage> storage)
{std::ifstream file(filename);if (!file.is_open()) {// 模拟文件打开失败的情况storage->setException(std::current_exception());return;}std::string content((std::istreambuf_iterator<char>(file)),std::istreambuf_iterator<char>());// 转换内容为大写for (char& c : content) {c = std::toupper(c);}// 假设这里输出转换后的内容std::cout << content << std::endl;
}int main()
{auto storage = std::make_shared<ExceptionStorage>();std::thread t(processFile, "nonexistentfile.txt", storage);// 等待线程结束t.join();std::exception_ptr ep = storage->getException();if (ep) {try {std::rethrow_exception(ep);} catch (const std::exception& e) {std::cerr << "Caught exception: " << e.what() << '\n';}}return 0;
}

在这个示例中,我们试图打开一个名为 nonexistentfile.txt 的文件,但实际上这个文件不存在。因此,当我们尝试打开文件时,std::ifstream 构造函数会抛出一个 std::runtime_error 异常(或类似异常)。这个异常被捕获,并通过 std::current_exception() 函数捕获当前的异常指针,然后将其存储在 ExceptionStorage 对象中。

当主线程调用 t.join() 并等待子线程完成后,它可以检查 ExceptionStorage 对象中的异常指针。如果存在异常指针,它会重新抛出异常,从而允许主线程处理这个异常。如果文件存在并且可以成功读取,那么不会有任何异常被存储,程序将正常运行。

案例扩展

我们可以在上面的基础上进一步扩展,以确保我们的代码更加健壮,并处理各种可能的情况。例如,我们可以添加更多的错误处理逻辑,以及提供更详细的错误信息。此外,还可以改进代码结构,使其更具模块化和可重用性。

下面是一个更详细的示例,包括错误处理和一些额外的功能:

  1. 增强错误处理:不仅处理文件不存在的情况,还处理其他潜在的错误,如权限问题等。
  2. 日志记录:增加日志记录功能,以便更好地跟踪错误和调试信息。
  3. 分离功能:将文件处理逻辑分离到一个单独的函数中,以提高代码的可读性和可维护性。

下面是改进后的代码:

#include <iostream>
#include <thread>
#include <exception>
#include <memory>
#include <fstream>
#include <string>
#include <sstream>// 用于在线程间传递异常的类
class ExceptionStorage {
public:void setException(std::exception_ptr ep) { m_exception = ep; }std::exception_ptr getException() const { return m_exception; }private:std::exception_ptr m_exception;
};// 用于记录日志的辅助函数
void logError(const std::string& message) {std::cerr << "[ERROR] " << message << std::endl;
}// 文件处理逻辑
void convertFileToUppercase(const std::string& filename, std::shared_ptr<ExceptionStorage> storage)
{std::ifstream file(filename);if (!file.is_open()) {// 捕获文件打开失败的情况try {std::ifstream file(filename); // 重新尝试一次,以确保捕获异常} catch (const std::exception& e) {logError("Failed to open file: " + filename);storage->setException(std::current_exception());return;}}std::stringstream buffer;buffer << file.rdbuf();std::string content = buffer.str();// 转换内容为大写for (char& c : content) {c = std::toupper(c);}// 输出转换后的内容std::cout << "Converted content: " << content << std::endl;
}int main()
{auto storage = std::make_shared<ExceptionStorage>();std::thread t(convertFileToUppercase, "nonexistentfile.txt", storage);// 等待线程结束t.join();std::exception_ptr ep = storage->getException();if (ep) {try {std::rethrow_exception(ep);} catch (const std::exception& e) {logError("Caught exception: " + std::string(e.what()));}}return 0;
}

说明

  1. 日志记录:添加了一个 logError 辅助函数,用于记录错误信息。
  2. 文件处理逻辑分离:将文件处理逻辑封装在 convertFileToUppercase 函数中,使得主函数更加简洁。
  3. 增强错误处理:在尝试打开文件时,增加了对异常的捕捉,并记录详细的错误信息。

通过这样的改进,代码变得更加清晰和易于维护。同时,通过日志记录,我们可以更容易地诊断问题所在。

😍😍 海量H5小游戏、微信小游戏、Web casualgame源码😍😍
😍😍试玩地址: https://www.bojiogame.sg😍😍
😍看上哪一款,需要源码的csdn私信我😍

————————————————

​最后我们放松一下眼睛
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词