10

如今,GNU C 库使用 DWARF2 展开来取消 pthread,因此 C++ 异常和 pthread 取消清理处理程序都通过公共调用框架展开过程调用,该过程在必要时为自动对象调用析构函数。但是,据我所知,仍然没有标准指定(POSIX)线程和 C++ 之间的交互,并且可能希望可移植的应用程序应该假设从取消清理上下文中抛出异常与调用一样longjmp未定义其中,取消具有具有非平凡析构函数的实时自动对象的线程也是未定义的行为。

是否有任何正在进行的标准化过程来解决这种交互,或者它是否有望在未来得到很好的定义?C++11 在其线程支持中是否有与 POSIX 线程取消类似的概念?

4

1 回答 1

13

作为一个坐在包含 WG14 (C)、WG15 (POSIX) 和 WG21 (C++) 的 ISO/IEC SC22 上的人,我可以告诉你,快速的答案是否定的,C++ 异常和线程取消不会互相看到任何时间很快。C11 和 C++11 没有提到线程取消,并且在大约十年后的下一个主要标准发布之前,如果不是极不可能的话,也极不可能认识到它。

更长的答案归结为标准如何运作。基本上ISO只能标准化每个人都可以同意的东西,而在线程取消方面人们并不同意。执行线程必须在每个可取消的系统调用之前转储状态的整个想法违背了现代软件开发的整个精神。它给编译器优化带来了巨大的问题,因为与 C++ 异常抛出不同,线程取消被定义为与调用 thread_terminate(self) 相同,这明确排除了做任何额外的事情(甚至取消处理程序在许多实现上都不能可靠地调用),并且我不认为线程取消支持者会不同意这是一个糟糕的解决方案。

问题是唯一合适的选择是重新发布带有异步完成变体的 POSIX i/o API。问题在于不同的 POSIX 实现对异步完成的看法非常不同。我的意思是,我们甚至不能就内核等待队列的标准达成一致,因此在实现异步 i/o API 之前还有很长的路要走。我有一个提议要对下一个标准 TC/TR 的内核等待队列进行一些移动,但是提议的对象故意非常简单化。

我们在 C11/C++11 中尝试做的是让线程 API 始终具有非阻塞版本 - 那里只有一个 API 不能非阻塞完成,即 thread_join()(有不是 thread_timedjoin()),我计划在获得奥斯汀工作组的批准后亲自提交勘误表。在所有其他情况下,人们总是可以构建一些投票效率不高但程序正确的东西。

从长远来看,就个人而言,我认为有充分的理由按照与 C++ 类似的语义向 C 添加异常处理。您不一定需要对象支持(实际上我个人也支持将非虚拟对象添加到 C 中),但是您将拥有堆栈展开 lambda 函数调用的概念。这将让我们通过正确定义的机制来正式化诸如线程取消之类的黑客攻击。它还使编写容错 C 更加容易和安全,因为它允许您在编写风时编写展开,并让旧 C 与新 C 透明地互操作。

关于从异常处理中抛出异常,我个人认为我们需要做一些比总是自动调用终止()更好的事情。由于展开可能会导致新对象的构建,或者实际上引发任何其他异常源,我个人非常希望在终止进程之前进行所有合理的尝试来展开整个堆栈。

因此,简而言之,预计 POSIX 线程取消将继续被视为未定义,并且从长远来看,它很有可能会被弃用以支持更好的东西。

顺便说一句,通常 POSIX 线程取消在实现之​​间是高度不可移植的,因此任何使用 POSIX 线程取消的代码都有效地依赖于特定于平台的行为,这与使用非 POSIX API 相同。如果您希望您的代码可移植,请不要使用 POSIX 线程取消。而是使用 select() 或 poll() 包括一个神奇的“请立即停止线程”文件描述符。在我自己的 C++ 代码中,我实际上有一个系统 API 包装宏,它测试这个神奇的文件描述符并抛出一个特殊的 C++ 异常。这可确保在包括 Windows 在内的所有平台上都有相同的行为。

于 2012-02-27T16:04:00.650 回答