gpt4 book ai didi

c++ - 在这种情况下从析构函数中抛出是否合适

转载 作者:搜寻专家 更新时间:2023-10-31 00:00:33 26 4
gpt4 key购买 nike

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

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 退出的任何异常都意味着工作失败。投入或不投入析构函数并不是行为的实质性改变。
  • 析构函数的后置条件不仅是资源清理,而且工作已经成功完成。因此,异常(exception)应该是适当的。

问题

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

最佳答案

不,不要从析构函数中抛出。调用 is_ok,捕获并忽略。与流的 close() 相同。

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

在实践中,这很少会带来不便。如果用户编写了一个具有多个返回点的函数,但(不幸的是)这是他们必须处理的问题,因为 C++ 没有提供让您为他们做这件事的方法。如果您认为这是反社会的,好吧,看看如果您在 C++11 中销毁 std::thread 而不先加入或分离它会发生什么。你赢了:-)

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

Even if the above is addressed, throwing or not throwing depending on whether stack is being unwound is a substantial change of behavior and should be discouraged.

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

从您指向 cppreference.com 的链接:

Any exception thrown while std::uncaught_exception() == true calls std::terminate.

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

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

关于c++ - 在这种情况下从析构函数中抛出是否合适,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12973758/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com