14

对于一些多线程代码,我想捕获所有异常并将它们传递给单个异常处理线程。这是消息传递框架:

#include <exception>

struct message
{
    virtual ~message() = default;
    virtual void act() = 0;
};

struct exception_message : message
{
    std::exception_ptr ep;

    virtual void act()
    {
        std::rethrow_exception(ep);
    }

    // ...
};

这是用例:

try
{
    // ...
}
catch (...)
{
    exception_message em { std::current_exception(); }
    handler_thread.post_message(em);
}

处理程序线程遍历它的所有消息和调用act(),它可以安装自己的 try/catch 块来处理所有发布的异常。

现在我想知道如果我将此消息的副本发送给多个接收者会发生什么。一般来说,消息可能有任意数量的收件人,因此我不想对异常传播消息进行任意限制。这exception_ptr被记录为“共享所有权”智能指针,并且rethrow_exception“不会引入数据竞争”。

所以我的问题是:通过将活动异常存储在 中exception_ptr、复制指针并rethrow_exception多次调用来复制活动异常是否合法?

4

1 回答 1

6

根据我对标准的理解,它是合法的。但是我会注意到,重新抛出不会复制异常,因此如果您修改它并同时从其他线程访问它,共享异常对象本身就会提交给数据竞争。如果异常是只读的(一旦抛出),那么您应该没有任何问题。

关于储存期限:

15.1 抛出异常[except.throw]

4异常对象的内存以未指定的方式分配,除非在 3.7.4.1 中注明。如果一个处理程序通过重新抛出而退出,则控制权将传递给另一个处理程序以处理相同的异常。异常对象在异常的最后一个剩余活动处理程序以除重新抛出以外的任何方式退出,或std::exception_ptr引用异常对象的最后一个类型 (18.8.5) 的对象被销毁后销毁,以较晚者为准。在前一种情况下,销毁发生在处理程序退出时,在处理程序的异常声明中声明的对象(如果有)销毁之后立即发生。在后一种情况下,销毁发生在std::exception_ptr返回的析构函数之前。

关于数据竞赛:

18.8.5 异常传播[propagation]

7为了确定是否存在数据竞争,对exception_ptr对象的操作应仅访问和修改exception_ptr对象本身,而不是它们引用的异常。使用引用相同异常对象rethrow_exceptionexception_ptr对象不应引入数据竞争。[注意:如果rethrow_exception重新抛出相同的异常对象(而不是副本),则对该重新抛出的异常对象的并发访问可能会引入数据竞争。引用特定异常的对象数量的变化exception_ptr不会引入数据竞争。——尾注]

关于rethrow

[[noreturn]] void rethrow_exception(exception_ptr p);

9 要求p不得为空指针。

10 次抛出:p 引用的异常对象。

于 2012-10-02T13:16:13.883 回答