【问题标题】:Unlock mutex required on cancellation point cleanup while waiting for condition variable?在等待条件变量时解锁取消点清理所需的互斥锁?
【发布时间】:2020-06-30 12:46:51
【问题描述】:

在 pthread 库中有取消点的概念。大多数可能会阻塞执行更长时间(或在某些资源上等待...)的系统函数可以被 pthread 取消点中止。

猜测有一些数据受条件变量保护,该条件变量在线程中执行,如下面的伪代码所示。该线程有一个设置清理过程,如果对该线程发出取消请求,则会调用该过程。

THREAD_CLEANUP_PROC {
    UNLOCK(mutex) // Is this unlock required?
}

THREAD_PROC {
    SET THREAD_CLEANUP = THREAD_CLEANUP_PROC
    LOOP {
        LOCK(mutex)
        WHILE (condition == false) {
            condition.WAIT(mutex) // wait interrupted, cancel point is called
        }
        // ... we have the lock
        UNLOCK(mutex)
        condition.NOTIFY_ALL()

        read(descriptor); // wait for some data on a file descriptor while lock is not acquired
    }
}

如果有人在等待条件变量时取消线程 (pthread_cancel()),有关 pthread_cond_wait 的文档说线程在获取锁时被解除阻塞,并在线程结束之前开始执行清理处理程序。

清理处理程序现在负责解锁该锁(互斥锁),这是真的吗?如果 - 就像在我的示例中一样 - 还有另一种阻塞方法,如 read,它在等待数据但没有获取锁的情况下阻塞?在这种情况下,read 也被解除阻塞,并且像以前一样调用清理处理程序。只有这一次清理处理程序不应解锁互斥锁。我对么。如果是这样,处理这种情况的最佳方法是什么?有没有应该遵循的共同概念?

【问题讨论】:

    标签: c pthreads locking condition-variable code-cleanup


    【解决方案1】:

    线程取消很麻烦。一般来说,你不应该这样做。

    在pthread库中有取消点的概念。

    是的。

    大多数可能会阻塞执行更长时间(或等待某些资源...)的系统函数可以被 pthread 取消点中止。

    不完全是。您描述的许多功能取消点。具有“延迟”取消类型的线程将在调用作为取消点的函数时中止,如果它当前是可取消的并且有一个取消请求未决。这并不意味着这样的函数可以被线程取消中断。具有“异步”取消功能的线程或多或少可以随时取消,包括在长时间运行的任务上阻塞时,但取消点在这种情况下无关紧要。

    如果有人在等待条件变量时取消线程 (pthread_cancel()),有关 pthread_cond_wait 的文档说线程在获取锁时被解除阻塞,并在线程结束之前开始执行清理处理程序。

    可以,前提是线程具有“延迟”取消类型。

    清理处理程序现在负责解锁该锁(互斥锁)是真的吗?

    是的。在这种情况下,线程在开始取消过程时会锁定互斥锁。如果它在终止之前没有解锁互斥锁,那么至少你会遇到很大的麻烦。某些类型的互斥锁(由 pthread 支持)可能会提供一种从这种情况中恢复的方法,但您最好避免这种情况。

    如果 - 就像在我的示例中一样 - 还有另一种阻塞方法,例如在等待数据但未获取锁的情况下读取该块?在这种情况下,读取也被解除阻塞,并且像以前一样调用清理处理程序。只有这一次清理处理程序不应解锁互斥锁。我说的对吗?

    同样,有多种类型的互斥锁,具体情况可能因您使用的不同而有所不同,但到目前为止,最好的选择是小心避免任何线程尝试解锁未锁定的互斥锁。

    如果是这样,处理这种情况的最佳方法是什么?

    处理这种情况的最佳方法是首先避免它。不要使用线程取消,尤其是对于容易出现此类问题的线程,这实际上很常见。

    相反,请谨慎编写多线程程序,以便为自己提供及时关闭线程或整个程序的替代方法。有很多这样的技术,比我在 SO 答案中合理总结的要多。

    【讨论】:

      【解决方案2】:

      您的代码必须编辑成如下所示:

      THREAD_CLEANUP_PROC {
          UNLOCK(mutex) // Is this unlock required? YES
      }
      
      THREAD_PROC {
          LOOP {
              LOCK(mutex)
              SET THREAD_CLEANUP_PUSH = THREAD_CLEANUP_PROC // After adquire the lock
              WHILE (condition == false) {
                  condition.WAIT(mutex) // wait interrupted, cancel point is called
              }
              // ... we have the lock
              THEAD_CLEANUP_POP(1) // This unlock the mutex and remove the cleanup
              // UNLOCK(mutex)
              condition.NOTIFY_ALL()
      
              read(descriptor); // wait for some data on a file descriptor while lock is not acquired
          }
      }
      

      【讨论】:

      • 当然,前提是线程具有“延迟”取消类型。如果它具有异步取消类型,则可以在锁定互斥锁和注册相应的清理处理程序之间,或者在弹出处理程序和解锁互斥锁之间取消它。此外,这对于任何复杂的程序都会变得非常混乱,因为需要大量不同的清理处理程序。
      猜你喜欢
      • 1970-01-01
      • 2015-07-12
      • 1970-01-01
      • 2012-12-25
      • 1970-01-01
      • 2010-11-06
      • 2012-06-13
      • 2012-05-25
      • 1970-01-01
      相关资源
      最近更新 更多