【问题标题】:C++, is set_terminate local to every thread?C ++,每个线程都是本地的 set_terminate 吗?
【发布时间】:2013-02-28 07:26:54
【问题描述】:

set_terminate/get_terminate 是否应该为 C++ 2011 或 C++ 2003 中的多个线程设置不同的终止异常处理器?

例如如果我有程序并将终止处理程序设置为func_1;然后我启动 3 个线程。什么是新线程中的终止处理程序?如果在每个线程中我将终止处理程序设置为第一个线程中的func_2,第二个线程中的func_3,依此类推。

N3242(C++ 2011 草案)在[handler.functions][support.exception]/[exception.terminate] 中没有提及它

PS:您可以回答 C++2011 或 C++2003 或这些标准的任何流行实现

PPS:对此有 FCD 评论...C++ FCD Comment Status Rev. 5 N3249 (2011)

GB 71    18.6.2.4 / 18.8.2.2 / 18.8.3.2   

std::set_new_handler()std::set_unexpected()std::set_terminate() 的线程安全性未指定,使得函数无法以线程安全的方式使用。

必须指定函数的线程安全保证,并应提供新接口,以便以线程安全的方式查询和安装处理程序。

LWG 1365 接受修改

见论文N3189

【问题讨论】:

  • DR1365 在发布 C++11 之前已通过采用我的回答中引用的数据竞争语言解决。

标签: c++ exception-handling c++11 language-lawyer c++03


【解决方案1】:

17.6.4.7p4 说:

调用set_*get_* 函数不会导致数据竞争。对任何set_* 函数的调用应与对同一set_* 函数和相应get_* 函数的后续调用同步。

这强烈暗示set_*get_* 函数在相同的全局状态下运行,即使从不同的线程调用也是如此。 18.8.3 下的所有段落都讨论了“当前处理函数”,没有其他提及线程;这表明处理函数是整个程序的一个属性;同样,17.6.4.7 具有:

2 - C++ 程序在执行期间可能会安装不同的处理函数 [...]
3 - C++ 程序可以通过调用以下函数来获取指向当前处理函数的指针 [...]

这些段落在程序的上下文中讨论当前处理函数,表明它是程序范围的而不是线程本地的。

【讨论】:

  • 当 set_* 和 get_* 在线程本地状态下工作时,也不会有数据竞争。并且至少有 3 个实现记录了线程本地状态:ms、ibm、sun。
  • 有 MS VS 6 和 SUN (smth old): msdn.microsoft.com/en-us/library/aa272914(v=vs.60).aspx & amath.unc.edu/sysadmin/DOC4.0/c-plusplus/c%2B%2B_ug/…;两者都谈到了线程本地terminate:ms:“在多线程环境中,终止函数是为每个线程单独维护的。”; sun:“每个线程都可以设置自己的 terminate() 或 unexpected() 函数。在一个线程中调用 set_terminate() 或 set_unexpected() 只会影响该线程中的异常。”。他们错了吗?
  • @osgx MS 文档正在讨论 <eh.h> 中的 C 库函数; C++ 库函数记录在msdn.microsoft.com/en-us/library/aa241196(v=vs.60).aspx。正如您所说,Sun 文档已经很老了;在 C++11 之前,C++ 不考虑线程,因此具有线程本地终止处理程序将是符合标准的扩展。
  • <eh.h> set_terminate() 函数的最新 MSDN 页面的链接:msdn.microsoft.com/en-us/library/t6fk7h29.aspx(这样事情就不会被 15 年前的实现所迷惑)。虽然此函数是 C 运行时的一部分,但文档说“set_terminate 与 C++ 异常处理一起使用...”,因此 C++ 运行时可能使用它从 <exception> 实现其 set_terminate()。另一方面,<exception> 中的 set_terminate() 的 MSDN 文档 - msdn.microsoft.com/en-us/library/ycf93beb.aspx - 没有提及线程。
  • msdn.microsoft.com/en-us/library/t6fk7h29.aspx for Visual Studio 2013 非常清楚地指出“在多线程环境中,终止函数是为每个线程单独维护的。每个新线程都需要安装自己的终止函数。因此,每个线程都在负责自己的终止处理。”
【解决方案2】:

在标准中它在下面说

18.8.3.2 set_terminate [set.terminate]

terminate_handler set_terminate(terminate_handler f) noexcept;

1 作用:将f指定的函数建立为终止异常处理的当前处理函数。

[[noreturn]] void terminate() noexcept;

2 效果:调用当前的 terminate_handler 函数。 [ 注意:在这种情况下,默认的 terminate_handler始终被视为一个可调用的处理程序。 ——尾注]

您可以看到terminate() 调用了current 终止处理程序,它在set_handler 部分很清楚地表明它用于终止 进程。当所有其他异常处理都失败时,无论从哪个线程运行,都会调用它。

只有一个终止处理程序,它总是从程序终止的地方调用。

【讨论】:

  • IBM says: “你可以通过提供一个调用 pthread_exit() 作为终止函数的函数来用线程级终止覆盖它。这会终止线程而不是进程。”
  • 并且...终止“异常处理”意味着不是终止进程。单线程也可以终止。
  • 但这是全局终止处理程序,指的是调用它的线程(通过调用pthread_exit),而不是特定于线程的处理程序。
  • @osgx pthread_exit 在这种情况下仍然是程序范围的终止处理程序; IBM 只是提供了一个扩展,以允许终止处理程序仅终止线程。
【解决方案3】:

标准没有具体说明; [set.terminate] 仅限州

[...] 用于终止异常处理的当前处理函数。

但没有提到“当前”是全局的还是每个线程的。所以这取决于实施。

例如,在 MSVC++ 中: https://msdn.microsoft.com/en-us/library/t6fk7h29.aspx

在多线程环境中,终止函数为每个线程单独维护。每个新线程都需要安装自己的终止函数。因此,每个线程都负责自己的终止处理。

【讨论】:

    【解决方案4】:

    C2003 没有线程,任何线程支持都是供应商扩展,因此只有供应商提供的文档才有答案。如果处理程序是每个线程的,则文档应该这样说。没有我知道的实现。

    C++2011 没有说明终止处理程序的每线程性质。每个线程都维护它没有什么意义,因为你不能在 C++11 中杀死一个线程。还有一个很好的理由(google kill+thread+c++11)。所以无论你做什么,程序都必须终止。看起来有不同的方法来终止程序,具体取决于请求它的线程不是任何人都需要的功能。

    【讨论】:

    • Sun/Oracle 做/做线程本地 set_terminate: Solaris Studio 12.2 "每个线程都可以设置自己的terminate() 或unexpected() 函数。同时调用set_terminate() 或set_unexpected()线程仅影响该线程中的异常。"
    • 它在 MSVC 中也是特定于线程的。不同的线程因不同的原因、代码的不同区域或实际上由 3rd-party 库启动。在终止进程之前想要执行一些特定于线程的活动是合理的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-23
    • 1970-01-01
    • 2020-11-17
    • 1970-01-01
    • 2012-04-18
    相关资源
    最近更新 更多