【发布时间】:2012-10-10 00:56:14
【问题描述】:
我有一个类将用户提供的工作分配给多个线程。类似(简化):
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退出的异常都意味着工作已失败。投掷或不投掷析构函数并不是行为的实质性改变。 - 析构函数的后置条件不仅是资源清理,而且工作已经成功完成。因此,例外应该是适当的。
问题
- 您认为在这种情况下从析构函数中抛出是否合适?
【问题讨论】:
-
使用回调函数?
标签: c++ exception destructor