我有一个类将用户提供的工作分配给多个线程。类似(简化):
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
意味着工作失败。扔或不扔析构函数并不是行为的实质性改变。 - 析构函数的后置条件不仅是资源清理,而且工作已经成功完成。因此,例外应该是适当的。
问题
- 您认为在这种情况下从析构函数中抛出是否合适?