0

我们知道,如果线程获得互斥锁但在它有机会释放它之前,线程被主线程挂起或被调度程序抢占,多线程代码可能会出现死锁?

我是使用 pthread 库的初学者,所以如果我的以下查询/建议的解决方案可能不可行或完全错误,请多多包涵。

void main()
{
    thread_create(T1,NULL,thr_function,NULL)

    suspend_thread(T1);

    acquire_lock(Lock1);<--- //Now here is a possible deadlock if thread_function acquried Lock1 before main and main suspended T1 before its release
    //Do something further;
}

void *thr_function(void *val)
{
    ///do something;
    acquire_lock(Lock1);
    //do some more things;
    //do some more things;
    release_lock(Lock1);

}

在我上面的下面的伪代码段中,线程运行时/编译器不能一起工作以确保获得互斥锁的线程是否被挂起/抢占然后它执行一些“清理代码”在它出来之前释放它持有的所有锁。编译器/链接器可以识别线程函数中获取、释放锁的位置,然后当线程在这两个位置之间挂起时(即在获取之后但在释放之前),线程函数中的执行应该通过某种' goto label;'跳转由运行时插入,label:线程将释放锁,然后线程被阻塞或发生上下文切换。[我知道如果一个线程获得了超过 1 个锁,那么跳过这些点来释放这些锁可能会变得混乱......]

但是基本的想法/问题是线程函数能否在它被阻塞或退出执行状态等待或其他状态之前为互斥锁、信号量执行必要的释放锁?

4

2 回答 2

2

不会。线程持有锁的原因是它可以使数据暂时不一致或看到该数据本身的一致视图。如果某个方案在线程再次使数据一致之前自动释放该锁,其他线程将获取锁,看到不一致的数据,然后失败。或者当该线程恢复时,它要么没有锁,要么拥有锁并看到不一致的数据本身。这就是为什么您只能在该线程的合作下可靠地挂起该线程。

考虑使用此逻辑将对象添加到受互斥锁保护的链表中:

  1. 获取保护链表的锁。
  2. 修改链接的头指针。
  3. 修改对象的 next 指针。
  4. 释放锁。

现在想象一下,如果在第 2 步和第 3 步之间暂停线程。如果锁被释放,其他线程将看到链接的头指针指向尚未链接到列表的对象。并且当线程恢复时,它可能将对象设置为错误的指针,因为列表已更改。

普遍的共识是挂起线程是如此邪恶,以至于即使您可能想要挂起线程的感觉也表明应用程序设计不正确。设计合理的应用程序几乎没有理由要挂起线程。(如果你不希望那个线程继续做它正在做的工作,你为什么要编码它来继续做那个工作?)

顺便说一句,调度程序抢占不是问题。最终,线程将再次被调度并释放锁。只要有其他线程可以向前推进,就没有坏处。如果没有其他线程可以向前推进,系统唯一能做的就是调度被抢占的线程。

于 2012-07-29T11:47:53.887 回答
0

避免这种死锁的一种方法是拥有一个全局的、互斥的变量,should_stop_thread该变量最终被true主线程设置为。

子线程定期检查变量,如果是,则以受控方式终止true。从这个意义上说,“受控”意味着所有数据(指针)都有效(再次)并且互斥锁被释放。

于 2012-07-29T11:49:46.630 回答