【问题标题】:What happens when a pthread dies while waiting on a mutex?当 pthread 在等待互斥体时死亡时会发生什么?
【发布时间】:2017-02-20 11:39:39
【问题描述】:

如果一个进程有三个线程,T1、T2 和 T3,并且所有三个线程都尝试获取互斥锁 M1 上的锁,我知道一个将获取互斥锁,而另外两个将等待直到它们获取互斥锁。

如果我使用 pthread_mutexattr_setrobust() 使线程变得健壮,那么我理解如果 T1 持有 M1,并且 T1 在释放之前终止,则 M1,T2 被唤醒并返回 EOWNERDEAD 值。然后 T2 可以清理受保护资源的状态。

现在,如果 T1 拥有 M1,而 T2 和 T3 正在等待 M1,会发生什么情况。 T2 终止。发生什么了?当 T1 释放 M1 时,互斥体是否直接进入 T3? T3 是否得到了 EOWNERDEAD,或者它只是在 T3 看来就像什么都没有发生并且不需要清理?似乎 T3 应该只占用 M1,因为 T2 不可能使线程进入不一致状态。有什么答案吗?这对我来说是一个智力上的兴趣问题,而不是试图解决一个特定的问题,因此将不胜感激有关该主题的进一步阅读的指针。

编辑:我不是想弄清楚如何正确地做到这一点,而是想弄清楚通常对于 pthread 实现,当 T2 被终止或被取消时会发生什么(对这两种情况都感兴趣,并且只开始了解它可能对结果产生的影响)。它是定义的行为吗?

我的目标平台是使用 Windows Unix 服务的 Windows,以防它依赖于平台,但我的兴趣是一般应该发生的事情。 https://technet.microsoft.com/en-us/library/bb463209.aspx.

【问题讨论】:

  • Windows 没有 pthreads,请编辑您的问题以阐明上下文。也许您的目标平台真的是 Cygwin 吗?还是您使用的是第三方库,例如 pthreads-win32?
  • ... 在任何情况下,正确的答案几乎肯定是终止 T2 会导致未定义的行为,因为在 Windows 上终止线程几乎总是会导致未定义的行为。只是不要。
  • 如果你使用pthreads-win32,注意pthread_kill is specifically documented“只支持零sig值,用于线程有效性检查”。
  • 只有当你有一个进程共享的互斥锁时,健壮的互斥锁才有意义,因为强制线程终止本质上是危险的。如果您使用 pthread_cancel(),您应该使用取消状态和清理处理程序来修复状态并在退出线程之前解锁互斥锁。 (pthread_kill() 不会终止单个线程,除非您有一个调用 pthread_exit() 的信号处理程序,这是相当危险的。)由于 pthreads-win32 似乎不支持进程共享互斥锁,这使得整个问题变得不那么有趣。
  • @jilles 我想你几乎给了我我正在寻找的确切答案。如果我在 T2 上使用 pthread_cancel() 并且没有做任何其他事情,那么当 T1 释放时互斥锁会发生什么?

标签: c windows multithreading


【解决方案1】:

注意:我的回答完全基于书面标准,而不是个人经验或相关专业知识。

取消一个pthread分为两种情况:

如果 pthread 配置为延迟取消(这是默认设置),那么根据标准的字面意思,取消请求将不会被处理,直到互斥体被声明(或未能声明)之后,因为 @如in section 2.9.5 of the standard所示,987654323@和朋友不在可取消的功能列表中。

我不确定各种实现是否真的以这种方式运行,因为这似乎是不可取的。如果一个实现在尝试声明互斥锁时确实允许取消(可能是因为pthread_mutex_lock 函数在内部使用列出的可取消函数之一),我希望它会导致互斥锁保持一致并且根据 Jille 的回答无人认领,但是该标准似乎没有明确要求。

如果 pthread 配置为异步取消,则行为未定义:

pthread_cancel()、pthread_setcancelstate() 和 pthread_setcanceltype() 函数被定义为异步取消安全的。

本卷 POSIX.1-2008 中的任何其他函数都不需要异步取消安全。

如果线程启用了异步取消并在执行非异步取消安全的函数期间被取消,则行为未定义。

似乎不可能终止单个 pthread。来自section 2.4.3:

当一个信号传递给一个线程时,如果该信号的动作指定终止、停止或继续,则整个进程应分别终止、停止或继续。

【讨论】:

    【解决方案2】:

    根据 POSIX.1-2008tc1 中 pthread_mutex_lock() 的规范:

    • 如果pthread_mutex_lock() 返回零或[EOWNERDEAD],则线程成为互斥锁的所有者。
    • 错误[EOWNERDEAD] 发生在包含拥有线程(或可选地,拥有线程)的进程在拥有互斥体时终止。

    在您的示例中,pthread_mutex_lock() 对 T2 不成功,因此 T3 将正确获取 M1。

    【讨论】:

    • 你确定这个实现实际上符合标准吗? OP 甚至没有指定他们正在使用什么实现。
    • 标准是否定义了这里应该发生的事情?我对特定平台的兴趣远不如应该发生的事情。如果可能的话,我会喜欢一个指针。
    【解决方案3】:

    您必须使用条件变量才能使其完美运行。我在下面复制粘贴了一些文档。

    条件变量允许线程同步到共享资源的值。通常,条件变量用作线程之间的通知系统。

    例如,您可以有一个计数器,一旦达到某个计数,您就希望激活一个线程。一旦计数器达到限制,激活的线程(或多个线程)将等待条件变量。活动线程在此条件变量上发出信号,以通知其他线程在此条件变量上等待/休眠;从而导致等待线程唤醒。如果您想通知所有等待条件变量的线程唤醒,您也可以使用广播机制。从概念上讲,这是由右图用伪代码建模的。

    当等待条件变量时,等待应该在循环中,而不是在简单的 if 语句中,因为会出现虚假唤醒。你不能保证如果一个线程被唤醒,它是信号或广播调用的结果。

    文档链接:http://randu.org/tutorials/threads/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-04
      • 2019-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多