C 僵局的快速修复?
我的教科书描述了死锁,如下图所示: 其中C 僵局的快速修复?,c,linux,multithreading,thread-safety,C,Linux,Multithreading,Thread Safety,我的教科书描述了死锁,如下图所示: 其中s和t是信号量和 void P(sem_t *s); /* Wrapper function for sem_wait */ void V(sem_t *s); /* Wrapper function for sem_post */ 我可以理解这是如何工作的,如果一个线程进入死锁区域,它就不能继续前进,永远卡住。教科书中提到了互斥锁排序规则: 如果对于程序中的每对互斥体(s,t),同时持有s和t的每个线程都以相同的顺序锁定它们,则程序是无死锁的。 例如
s
和t
是信号量和
void P(sem_t *s); /* Wrapper function for sem_wait */
void V(sem_t *s); /* Wrapper function for sem_post */
我可以理解这是如何工作的,如果一个线程进入死锁区域,它就不能继续前进,永远卡住。教科书中提到了互斥锁排序规则:
如果对于程序中的每对互斥体(s,t),同时持有s和t的每个线程都以相同的顺序锁定它们,则程序是无死锁的。
例如,我们可以通过先锁定s,然后锁定t来修复死锁
每个线程。下图显示了生成的进度图:
但我有一个快速解决方案可能有效,我不确定我是否正确:
我们可以只添加一些简单的语句,比如single或int test=0
,这将在两个禁止区域之间创建一个垂直间隙,因此线程最终可以通过该间隙,如下图所示:
我的方法在技术上正确吗
我们可以只添加一些琐碎的语句,例如single;或者P和V操作之间的int test=0,这将在两个禁止区域之间创建一个垂直间隙,因此线程最终可以通过该间隙,如下图所示
首先“;”(您希望编译器生成的代码有什么?)或inttest=0
不会产生间隙,即使执行或线程是异步的,也不能对它们的执行进行假设,这就是为什么要使用互斥体
因此,如果需要获取多个互斥体,则必须在所有线程中以相同的顺序执行。显然,互斥锁并不是产生死锁的唯一方法,所有同步点都必须考虑进去。书中的两张图片显示了重叠的矩形。重叠区域对应于任一线程可以同时拥有两个互斥体的事实。另一方面,您绘制的图片显示的是不重叠的矩形。如果我们以与本书图片相同的方式解释您的图片,那么非重叠性应该意味着两个线程都不能同时拥有两个互斥体
是的,重新设计代码,使任何线程一次都不会锁定多个互斥锁,这是防止线程在获取互斥锁时死锁的一种保证方法,但这也可能使程序更难有效地执行您需要它执行的任何操作。这就是反模式
关于“死锁的快速修复?”
僵局是个问题。对于体系结构问题没有快速解决方法。像这样对死锁进行推理是不必要的复杂。死锁只是当多个线程在同一资源上等待时发生的。无需绘制图表,也无需攻读该学科的博士学位。一个常见的例子是:同时,线程A
获取互斥体A
,线程B
获取互斥体B
,线程C
获取互斥体C
。如果B
然后等待a
被释放,而C
等待B
和a
等待C
,我们就实现了死锁。避免这种情况的方法是在设计代码时使用常识。常识只需要很少的资源,但对许多资源来说,你必须组织起来。画一两张图表。不过我从没见过这种。它们很有趣。@Lundin,您的死锁示例与您的说法不符,“死锁只是当多个线程等待同一资源时。”当线程等待同一事物时,我们称之为“争用”,这并不总是一件坏事。另一方面,死锁的名称中有“dead”是有充分理由的。@Lundin即使使用“常识”也会使避免死锁变得过于复杂。避免死锁很简单——总是以相同的顺序获取锁。时期并且永远不要升级锁-例如将只读锁转换为写锁。在我看来,如果您不能指定并强制执行特定的锁定顺序,那么您的设计就太复杂了,不可能可靠——我的意思是,在所有方面都不可靠,而不仅仅是在避免死锁方面。