是否可以通过这种方式避免多线程代码可能的死锁



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

我是使用 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 个锁,跳过这些点以释放这些锁可能会变得混乱......]

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

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

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

  1. 获取保护链表的锁。
  2. 修改链接的头指针。
  3. 修改对象的下一个指针。
  4. 松开锁。

现在想象一下,如果有什么东西在步骤 2 和 3 之间挂起线程。如果释放锁,其他线程将看到链接的头指针指向尚未链接到列表的对象。当线程恢复时,它可能会将对象设置为错误的指针,因为列表已更改。

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

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

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

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

最新更新