我们知道,如果线程获得互斥锁,但是在它有机会释放它之前,多线程代码有可能死锁的祸害,线程被主线程挂起或被Scheduler抢占?
我是使用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);
}
在我上面的伪代码段中,线程运行时/编译器可以一起工作,以确保一个已经获得互斥锁的线程被挂起/抢占然后执行一些&#39;清理代码&#39;在它出来之前释放它所持有的所有锁。编译器/链接器可以识别线程函数中获取,释放锁定的位置,然后当线程在这两个位置之间暂停时(即在获取之后但在释放之前),线程函数中的执行应该通过某种&#跳转39; goto label;
&#39;由运行时插入,label:
线程将释放锁,然后线程被阻塞或上下文切换发生。 [我知道如果一个线程获得了超过1个锁,那么跳过这些点可能会变得混乱以释放这些锁...]
但基本的想法/问题是,线程函数是否可以在被阻塞之前对信号量,信号量进行必要的释放,或者在执行状态之前等待或等待其他状态?
答案 0 :(得分:2)
没有。线程持有锁的原因是它可以使数据暂时不一致或者看到该数据本身的一致视图。如果某个方案在线程再次使数据一致之前自动释放该锁,则其他线程将获取锁,查看不一致的数据,并失败。或者当该线程被恢复时,它将没有锁或具有锁并且看到不一致的数据本身。这就是为什么你只能通过该线程的合作可靠地挂起一个线程。
考虑这个逻辑,将对象添加到受互斥锁保护的链表:
现在假设某些事情是在第2步和第3步之间挂起线程。如果锁被释放,其他线程会看到链接的头指针指向尚未链接到列表的对象。当线程恢复时,它可能会将对象设置为错误的指针,因为列表已更改。
普遍的共识是,暂停线程是如此邪恶,甚至你可能想要暂停一个线程的感觉表明不正确的应用程序设计。实际上没有理由设计合理的应用程序想要挂起一个线程。 (如果您不希望该线程继续执行它正在进行的工作,为什么要对其进行编码以便首先继续执行该工作?)
顺便说一下,调度程序抢占不是问题。最终,线程将再次被安排并释放锁。只要有其他线程可以推进进展,就不会造成任何伤害。如果没有其他线程可以推进进度,那么系统唯一可以做的就是安排被抢占的线程。
答案 1 :(得分:0)
避免这种死锁的一种方法是使用一个全局的互斥变量should_stop_thread
,最终由主线程设置为true
。
子线程定期检查变量,如果是true
,则以受控方式终止。在这种意义上,“受控制”意味着所有数据(指针)都有效(再次)并释放互斥锁。