【问题标题】:What does it mean to POSIX that a thread is "suspended"?一个线程被“挂起”对 POSIX 意味着什么?
【发布时间】:2015-09-24 19:10:03
【问题描述】:

a recent question 的评论过程中,出现了一个附属问题,即在什么时候可以对具有可取消性PTHREAD_CANCEL_DEFERRED 的pthreads 线程的取消请求采取行动。对the standard 的引用和一些律师接踵而至。我不太关心我的 cmets 是否在这个问题上弄错了,但我确保我正确理解了 POSIX 的规定。

标准中最相关的部分说

当一个线程启用了可取消性并且以该线程为目标发出了一个取消请求,然后该线程调用任何作为取消点的函数 [...],取消请求应在函数返回。如果线程启用了可取消性,并在线程在取消点挂起时以该线程为目标发出取消请求,则该线程将被唤醒并对取消请求进行处理。

但是,线程被“暂停”是什么意思? POSIX 明确定义了进程的术语,但据我所知,没有定义线程的术语。另一方面,POSIX 将线程挂起记录为少数函数的行为之一,包括但不限于与同步对象相关的一些函数。那么是否应该得出结论,这些共同作为该术语的相关定义?

由于这一切都与引发此查询的问题有关,鉴于 POSIX 并未将线程暂停指定为 read()fread() 或任何一般文件或流 I/ 的行为的一部分O 函数,如果一个线程由于 I/O 阻塞而没有进展,这是否一定意味着它为了取消的目的而被“挂起”?

【问题讨论】:

  • IEEE 1003.1 (POSIX) 标准明确声称它符合 ISO/IEC 9899 (C) 标准。 C 标准引用了ISO/IEC 2382 (Information Technology Vocabulary) 的术语。不幸的是,甚至 ISO/IEC 2382 都没有定义suspended,甚至在某些定义中使用了这个词。我从中得出的结论是,“suspended”被认为是一个核心英语单词,适合用于解释。我个人对挂起的解释是“当前未使用处理器时间的线程的状态”。
  • @IwillnotexistIdonotexist,与 C 的对齐是一个有用的观察,谢谢。然而,我发现“当前未使用处理器时间”是一个令人不安的解释,因为它似乎涵盖了可能正在运行但已被抢占的线程。这似乎不匹配 - 每个迹象都表明线程暂停是一种线程被阻止运行的状态,即使 CPU 本来可以用于它。确实可能是 C 和 POSIX 标准假定应该应用自然解释,但细节很重要,而且它们不清楚(对我来说)。
  • 我很自然地认为没有运行的可运行线程因为缺乏更好的词而被暂停。在负载很重的n-core 处理器上,可能有>n runnable 进程,但最多可以使其中的n runnable;所有其他人都必须未运行,或者按照POSIX可能的规定,“他们的执行被暂停”等待轮到他们。你怎么看?等待正在运行的线程的时间段到期的可运行线程是否已挂起?一个运行中的线程,其量程到期,即使仍然可以运行,也被关闭了呢?断点线程呢?
  • POSIX 的Definitions 具有以下优点:3.281 抢占进程(或线程)A由于另一个线程变得可以以更高的优先级运行而导致其执行被挂起的正在运行的线程。 3.287 优先级反转非自愿挂起(等待事件或时间延迟)的线程未运行而低优先级线程正在运行的情况。这种较高优先级线程的阻塞通常是由共享资源的争用引起的。
  • “等待事件或时间延迟”似乎很有帮助。 aio_suspend 函数的描述开始,aio_suspend() 函数将暂停调用线程,直到列表参数引用的至少一个异步 I/O 操作完成,直到一个信号中断函数,或者,如果 timeout 不为 NULL,直到 timeout 指定的时间间隔已经过去。 我只能同意“暂停”的预期定义是“暂停/中断”,没有更正式的定义到位。

标签: c posix language-lawyer


【解决方案1】:

正如你所说,挂起的线程是一个在套接字读取时被阻塞、等待信号量变为可用等的线程。

鉴于 POSIX 实现在棘手的边缘有所不同,并且线程有可能在不是取消点的函数中被阻塞,因此依赖于要移植的代码中的取消可能麻烦多于它的价值。

我从来没有使用过它,我总是选择让代码明确指示线程终止(通常是一条管道或队列中的消息)。这对于通信顺序流程或 Actor 模型系统非常容易。

这样清理可以在自己的控制下完成,根据需要释放内存等。我不知道取消的线程是否会清理其内存(我怀疑不会),或者是否有 at_exit() 类型的选项(可能有)。总的来说,如果线程只有一种退出方式,我认为应用程序的行为会得到更彻底的控制。

==编辑==

@JohnBollinger,

If a thread has cancelability enabled and a cancellation request is made with the thread as a target while the thread is suspended at a cancellation point 使用的语言可以解释为IF a thread has cancelability enabled AND IF cancelled and IF implementation suspends blocked threads AND IF the thread is blocked THEN the thread shall be awakened...。换句话说,他们把它留给了 POSIX 子系统的实现者。

Cygwin 的 select() 实现不会(或至少不会)导致线程被挂起。相反,它为每个文件描述符生成一个轮询线程来测试可发信号的活动,因为在 Windows 中根本没有类似 select() 的东西(它接近,但没有雪茄。Win32 select() 仅适用于套接字)。早在 1980 年代 select() 的实现也经常以这种方式工作。

可能是因为这样的原因,POSIX 不愿意明确定义线程何时挂起。从历史上看,select() 的许多实现都是这样的,这使得标准委员会决定线程何时可能会或可能不会被暂停是一个雷区。当然,由 select() 引起的复杂性也适用于进程,但是由于 POSIX 确实定义了一个挂起的进程,所以它们不能/没有将定义扩展到线程似乎很奇怪。

这可能取决于线程是如何实现的;您可以想象有一个不使用操作系统线程的 POSIX 实现(有点像 ADA 的早期实现,当时操作系统根本不做线程),并且在这样的实现中,阻塞的线程可能不会被挂起(在不占用 CPU 周期的意义上)。

【讨论】:

  • “如您所说,挂起的线程是在套接字读取时被阻塞,等待信号量变为可用等的线程”。其实不,我这么说。特别是,我怀疑 POSIX 是否定义了一个在读取被挂起时阻塞的线程。我很高兴听到您提出这种说法的依据——这正是我正在寻找的东西。
  • 可以通过(惊喜!)pthread_cleanup_push()/pthread_cleanup_pop() 来清理线程取消。
  • @JohnBollinger,这很公平。我也没有说 POSIX 是这样定义的,但是当然,实现挂起被阻塞的线程是很常见的。我已添加到我的答案中
【解决方案2】:

线程上下文中挂起的定义:

3.107 条件变量

一个同步对象,它允许线程重复地暂停执行,直到某个关联的谓词变为真。在条件变量上暂停执行的线程被称为在条件变量上被阻塞。

发件人:http://pubs.opengroup.org/onlinepubs/9699919799/

这不是一个直接的答案,只是一个定义——太大而无法评论。已阻止 == 暂停。

【讨论】:

  • 是的,我已经看到了。我认为它定义了pthread_cond_wait() 的行为以包括线程暂停。但是,其他函数(wait()sleep()、...)也被记录为导致线程挂起,因此这不是一个完整的定义。
  • '同步对象'不一定是代码本地的 - 它可能是内核实现的对象。我们需要确定的是定义发展背后的讨论(意图)。有什么帮助吗?
  • 我已经断断续续地研究了 POSIX 的各个方面,但恐怕我对它的发展没有特别的了解。在这种情况下,我认为从开发人员的角度来看有充分的理由希望包容地定义“暂停”,但从标准编写者和实施者的角度来看,有充分的理由希望对其进行狭义的定义。
  • 另外,如果人们接受它作为线程暂停的实际定义,那么我们必须得出结论,POSIX 需要在等待条件变量方面实现各种功能,但它没有定义是否其他可能阻塞的函数实际上会挂起调用线程。
【解决方案3】:

read、fread 和friends 是系统调用,因此它们将执行上下文切换并从内核上下文执行,直到这些函数完成。中断内核上下文超出了 pthread 的范围,因此它们不会导致取消。

我没有它的参考,但据我所知,Posix 线程上下文中的线程挂起与它的同步对象(如 futex 的)有关。

【讨论】:

  • 谢谢,但我正在寻找基于文档的参数,而不是基于特定的实现
  • 呃,那些系统调用可中断的。 man read: [...]It is not an error if this number is smaller than the number of bytes requested; this may happen for example [...] because read() was interrupted by a signal.[...] EINTR The call was interrupted by a signal before any data was read; see signal(7).
猜你喜欢
  • 2023-01-03
  • 2017-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-11
  • 1970-01-01
  • 2010-11-07
相关资源
最近更新 更多