如何摆脱linux中的僵局

时间:2015-07-26 12:39:25

标签: linux multithreading deadlock

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

主题A:

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label

class Example(App):
     def build(self):
          self.root = FloatLayout()
          self.label = Label(text="I'm centered :(", pos=(0,0), size_hint=(1.0,1.0), halign="left")
          self.label.text_size = self.label.size #no horizontal change
          self.root.add_widget(self.label)
          return self.root

Example().run()

主题B:

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

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

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

2 个答案:

答案 0 :(得分:1)

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

然后该线程负责清除任何不一致的状态。如果可以恢复,则线程应在pthread_mutex_consistent(3)之前的任何时间调用pthread_mutex_unlock(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)

的联机帮助页

答案 1 :(得分:0)

如果你想让线程自行清理,你通常需要自己要求或自己动手。

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

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

当所有者死亡时,确实存在其他锁定类型会自动解锁(例如flock()锁定,或强大的互斥锁,如FilipeGonçalves所示),但在有人丢失锁定的情况下,您几乎无法保证状态一致性并且需要在他们之后进行清理