2

我有一个类将用户提供的工作分配给多个线程。类似(简化):

class MT {
public:

    MT();

    void work1(/*args*/);
    void work2(/*args*/);
    void work3(/*args*/);

    //waits until all threads complete, returns whether the work has been completed
    //successfully, throws any exceptions that have been raised in the threads.
    bool is_ok(); 

    ~MT();

private:
    // thread pool
}

用户使用该类如下:

void foo()
{
    MT mt;
    mt.work1(/*userdata*/);
    mt.work2(/*userdata*/);
    mt.work1(/*userdata*/);
    status = mt.is_ok();
    if (status) {
        mt.work3(/*userdata*/);
        //...
    }
    //...
}

该类永远不是某个对象的一部分,始终存储在堆栈中。

问题

我想以某种方式发出在其他线程中工作时引发的任何异常。不幸的是,我知道一个线程是否在加入后才成功完成。因此,我必须在以下两种选择之间进行选择:

  • 加入析构函数中的线程MT并抛出工作时出现的异常(如果有)。如果抛出了多个异常,请选择来自最早任务的异常。如果调用析构函数进行堆栈展开(我们可以使用 进行检查std::uncaught_exception,吞下任何异常以防止terminate().

  • 指示用户始终is_ok在析构函数之前调用。

我认为第一个选项更干净,因为用户不需要调用任何东西。但是,通常非常不鼓励从析构函数中抛出。提出的论点是:

  • 从析构函数中抛出是危险的,因为这可能在堆栈展开期间发生并导致terminate().
  • 即使解决了上述问题,根据堆栈是否正在展开而投掷或不投掷也是行为的重大变化,应该不鼓励。
  • 异常表示未满足后置条件。析构函数的后置条件是资源清理,这在任何情况下都必须是可能的。

不知何故,我倾向于认为上述论点不适用于这里:

  • 我正确处理堆栈展开问题
  • 由于使用模式,任何退出的异常都foo意味着工作失败。扔或不扔析构函数并不是行为的实质性改变。
  • 析构函数的后置条件不仅是资源清理,而且工作已经成功完成。因此,例外应该是适当的。

问题

  • 您认为在这种情况下从析构函数中抛出是否合适?
4

1 回答 1

7

不,不要从析构函数中抛出。呼叫is_ok、捕捉和忽略。与close()流相同。

用户is_ok 如果想确定工作是否已经完成,可以调用,失败时会抛出异常。

在实践中,这很少不方便。如果用户编写了一个具有多个返回点的函数,但(不幸的是)这是他们必须处理的问题,因为 C++ 没有为您提供为他们执行此操作的方法。如果你认为这是反社会的,那么,看看如果你std::thread在 C++11 中破坏 a 而不先加入或分离它会发生什么。你打败了:-)

因此,用户调用is_ok所有非错误退出路径。当已经有异常时,用户不会打扰调用它,因为他们无论如何都无法处理另一个异常。同样,这与close()流相同:如果您想查看写入缓冲流的错误,那么您只需要显式关闭或刷新。

即使解决了上述问题,根据堆栈是否正在展开而投掷或不投掷也是行为的重大变化,应该不鼓励。

至少在 C++03 中也无法正确执行。正如 Herb Sutter 解释的那样,我不知道 C++11 在这方面是否有任何改变,但std::uncaught_exception 没有告诉你你需要知道什么。

从您到 cppreference.com 的链接:

在 std::uncaught_exception() == true 调用 std::terminate 时抛出的任何异常。

我很确定这是错误的。terminate如果异常逃脱了作为堆栈展开的一部分调用的析构函数,则调用它,但不会仅仅因为析构函数在展开期间抛出并捕获异常而调用它。

如果调用者认为他们可以对析构函数中抛出的异常做一些有用的事情,那么他们可以编写一个is_ok析构函数中调用的辅助类,并将其放在您的对象上。如果你认为你的类可以用同样的异常做一些有用的事情,那么你可以做一些事情,而不仅仅是在你的析构函数中忽略它,但你仍然不应该让它离开析构函数。

于 2012-10-19T12:00:46.097 回答