【问题标题】:How to come out of a deadlock in linux如何摆脱linux中的死锁
【发布时间】:2015-07-26 12:39:24
【问题描述】:

在多线程系统上,如果两个线程在锁定互斥锁后想要在共享内存上工作。

线程A:

pthread_mutex_lock(&mutex)  
.......   //Memory corruption or Assert and thread exits  
pthread_mutex_unlock(&mutex)  

线程 B:

pthread_mutex_lock(&mutex)  
.......  
pthread_mutex_unlock(&mutex)  

如果线程 A 先获取互斥体并由于内存损坏或断言而退出,线程 B 将永远等待导致死锁。

  1. 一旦发生这种僵局,我有什么办法可以摆脱这种僵局?
  2. 有没有其他更安全的方法,类似于我可以使用的互斥锁?

【问题讨论】:

  • 确保一个线程有一个需要在退出时释放的所有资源的列表。确保在退出时释放所有资源。
  • 如果线程 A 由于“内存损坏”而退出,您已经有未定义的行为,没有什么可以帮助您。 IIRC 断言失败将退出进程,而不仅仅是线程。

标签: linux multithreading deadlock


【解决方案1】:

您可以在互斥体上设置ROBUST 属性。使用健壮的互斥锁,如果获取它的线程由于某种原因退出而没有解锁它,互斥锁进入一个特殊状态,下一个尝试锁定它的线程将获得EOWNERDEAD

然后,该线程负责清理任何不一致的状态。如果可以恢复,线程应该在pthread_mutex_unlock(3)之前的任何时间调用pthread_mutex_consistent(3),以便其他线程可以像以前一样使用它。如果无法恢复,则应在不调用pthread_mutex_consistent(3) 的情况下解锁互斥体,使其进入不可用状态,唯一允许的操作是销毁它。

请注意,即使返回了EOWNERDEAD,互斥锁也会被锁定(我认为这是pthread_mutex_lock(3) 返回错误但锁定互斥锁的唯一条件。

要设置ROBUST 属性,请在初始化互斥体属性实例后使用pthread_mutexattr_setrobust(3)。请记住,这必须在初始化互斥体之前完成。所以,类似:

pthread_mutex_t mutex;
pthread_mutexattr_t mutex_attrs;

if (pthread_mutexattr_init(&mutex_attrs) != 0) {
    /* Handle error... */
}
if (pthread_mutexattr_setrobust(&mutex_attrs, PTHREAD_MUTEX_ROBUST) != 0) {
    /* Handle error... */
}
if (pthread_mutex_init(&mutex, &mutex_attrs) != 0) {
    /* Handle error... */
}

然后你可以像这样使用它:

int lock_res = pthread_mutex_lock(&mutex);

if (lock_res == EOWNERDEAD) {
    /* Someone died before unlocking the mutex
     * We assume there's no cleanup to do
     */
    if (pthread_mutex_consistent(&mutex) != 0) {
        /* Handle error... */
    }
} else if (lock_res != 0) {
    /* Some other error, handle it here */
}

/* mutex is locked here, do stuff... */

if (pthread_mutex_unlock(&mutex) != 0) {
    /* Handle error */
}

有关更多信息,您可以查看 pthread_mutex_consistent(3)pthread_mutex_getrobust(3) / pthread_mutex_setrobust(3) 的手册页

【讨论】:

    【解决方案2】:

    如果您希望线程自行清理,通常需要请求或自己进行。

    例如,您可以使用pthread_cleanup_push() and pthread_cleanup_pop() 来确保在线程使用pthread_exit() 退出或被取消(通过pthread_cancel())时进行清理。

    这个 API 的缺点是每对调用必须在同一个函数和同一个词法嵌套级别中(例如在你的线程的入口函数中)。 我不知道是否允许您从信号处理程序中取消线程,因此您可能无法使用该 API 处理此问题,并且这些类型的错误和断言通常会使整个进程退出(尽管可能如果你正在实现自己的断言变体,它可以调用pthread_exit())。

    确实存在在所有者死亡时自动解锁的其他锁类型(例如 flock() 锁,或 Filipe Gonçalves 所示的健壮互斥锁),但在有人丢失锁并且将需要在他们之后清理

    【讨论】:

    • 这并不完全适合这里。你怎么知道你是否必须释放互斥锁?通常,当调用清理处理程序时,您不知道您在代码中的哪个位置。也许我错过了什么?
    • @FilipeGonçalves 如果这实际上适用于您的代码,那么您可能会有类似lock(mtx); cln_push(unlockfnc, mtx); /* Do stuff */ ; cln_pop(0); unlock(mtx) /*Could also pass non-zero to pop instead*/ ; 的内容。清理处理程序是堆叠的,人们希望内部代码也使用这些处理程序以返回一致状态(和/或发出不一致的信号)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-21
    • 2015-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多