C++ 中多线程和异常安全之间的紧张关系是什么?有好的指导方针可以遵循吗?线程是否因未捕获的异常而终止?

有帮助吗?

解决方案

相信C ++标准不会使多线程的任何提及 - 多线程是一个平台特定的特征

我不完全知道什么是C ++标准说对一般捕获的异常,但根据的这个页面,会发生什么是平台的定义,你应该编译器的文档中找到。

在一个快速和肮脏的测试我与克++ 4.0.1(i686的-苹果darwin8-G ++ - 4.0.1是特异性的),结果是terminate()被调用时,它可以杀死整个程序。我使用的代码如下:

#include <stdio.h>
#include <pthread.h>

void *threadproc(void *x)
{
  throw 0;

  return NULL;
}

int main(int argc, char **argv)
{
  pthread_t t;
  pthread_create(&t, NULL, threadproc, NULL);

  void *ret;
  pthread_join(t, &ret);

  printf("ret = 0x%08x\n", ret);

  return 0;
}

g++ threadtest.cc -lpthread -o threadtest汇编。输出是:

terminate called after throwing an instance of 'int'

其他提示

的C ++ 0x将具有语言支持线程,以便,当一个工作线程抛出异常产卵线程可以捕捉或重新抛出其之间传输异常。

从提案:

namespace std {

    typedef unspecified exception_ptr;

    exception_ptr current_exception();
    void rethrow_exception( exception_ptr p );

    template< class E > exception_ptr copy_exception( E e );
}

未捕获的异常将调用terminate()继而调用terminate_handler(其可以由程序被设置)。默认情况下,调用terminate_handler abort()

即使你覆盖默认terminate_handler,标准说,你提供日常“应不返回给调用终止程序的执行”(ISO 14882-2003 18.6.1.3)。

因此,简言之,未捕获的异常将终止该程序而不只是线程。

至于线程安全的云,如亚当罗森菲尔德说,这是这不是由标准解决特定于平台的事情。

这是二郎存在单个最大原因。

我不知道该约定是什么,但恕我直言,是因为二郎般越好。使堆对象不可变和设置某种消息传递协议的线程之间进行通信。避免锁。确保消息传递是异常安全。保持在堆栈上尽可能多的状态的东西。

正如其他人讨论,并发(尤其是线程安全,)是一种架构的问题,会影响您设计系统和应用程序。

不过,我想借你对异常安全性和线程安全之间的矛盾问题。

目前类级别线程安全需要改变接口。就像异常安全一样。例如,它是常用于类的引用返回到内部变量,说:

class Foo {
public:
  void set_value(std::string const & s);

  std::string const & value() const;
};

如果foo是由多个线程共享,麻烦等着你。当然,你可以把一个互斥体或其他锁访问富。过了不久,所有的C ++程序员会想包装富成“ThreadSafeFoo”。我的论点是,对于富接口应改为:

class Foo {
public:
  void set_value(std::string const & s);

  std::string value() const;
};

是,它是更昂贵,但它可以由线程安全的内部富锁。 IMNSHO这产生一定量的线程安全和异常安全性之间的张力。或至少,需要作为用作共享资源的每个类需要两种光下检查以执行更多的分析。

一个典型的例子(不记得在那里我看到它首先)是在STD库中。

下面是你如何从队列中弹出的东西:

T t;
t = q.front(); // may throw
q.pop();

相比此接口是稍微钝:

T t = q.pop();

但是做,因为T拷贝赋值可以抛出。如果复制抛出流行发生后,该元素从队列中丢失,而且再也无法恢复。但是,由于该元素被弹出之前的副本发生,你可以把任意处理周围的拷贝在try / catch块前()。

缺点是你无法实现的队列是线程安全的,因为与所涉及的两个步骤的std ::队列的接口。什么是良好的异常安全(分离出可以抛出步骤),现在不好对多线程的。

在异常安全你的主要救星是指针操作没有罚球。同样,指针操作可以由在大多数平台上的原子,因此他们往往可以在多线程代码的救星。您可以让您鱼与熊掌兼得,但它是真的很难。

我注意到有两个问题:

  • 在 Linux 上的 g++ 中,线程的终止 (pthread_cancel) 是通过抛出“未知”异常来完成的。一方面,这可以让你在线程被杀死时很好地清理。另一方面,如果您捕获该异常并且不重新抛出它,您的代码将以 abort() 结束。因此,如果您或您使用的任何库杀死线程,您就不能

    抓住(...)

没有

throw;

在你的线程代码中。 这里 是对网络上此行为的引用:

  • 有时你需要在线程之间传输异常。这不是一件容易的事情 - 我们最终做了一些黑客,当正确的解决方案是您在进程之间使用的编组/解组时。

我不建议让任何异常仍然未被捕获。总结在包罗万象的,可以更加妥善地(或至少冗长)关闭该程序处理您的顶级线程功能。

我觉得最重要的是要记住,从其他线程捕获的异常不显示给用户,或者在主线程中抛出。所以,你必须扭曲所有应该比上用try / catch块主线程不同的线程上运行的代码。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top