有向图上无死锁的互斥锁

时间:2016-09-14 02:31:22

标签: multithreading pthreads mutex deadlock race-condition

假设我正在编写一个多线程服务器。服务器维护节点的有向图,其中每个节点可以指向并由许多其他节点指向。客户端可以添加新节点,在节点之间添加新指针,也可以删除它们。引用计数用于垃圾收集。如果两个客户端尝试执行可能导致竞争条件的操作,则应阻止一个客户端。阻塞频率应在客户端之间和节点之间保持一致

如何使用互斥原语来完成此操作并避免竞争和死锁?我似乎无法创建一个简单的全局通行权排名,因为这有利于某些客户或节点。

如果它改变了答案,那么它是使用POSIX线程接口在C中实现的。这是节点定义的一种方式:

struct node {
    pthread_mutex_t mut;
    int refs; /* reference counting */
    void *content; /* immutable */
    struct node *links[];
}

我尝试使用递归互斥锁来锁定需要操作的每个节点(假设客户端将节点链接到自身),但我不确定这是否正确。

1 个答案:

答案 0 :(得分:2)

答案:非常仔细

引用计数需要链接列表中的单独互斥锁。 (或者只是在ref count上使用原子操作。)否则你最终会同时持有两个可以解锁的互斥锁。引用计数的互斥锁不会计数"计数"作为潜在的死锁,因为如果它只修改了引用计数,那么它就是一个终点 - 你在持有它时永远不会抓住另一个锁,因此它不会导致死锁。锁,incr,解锁。 (或者更好 - atomic incr)在持有ref-count锁时没有别的东西。

之前"使用"您需要增加其引用计数的节点。但在此之前,您需要知道节点是否即将被删除 - 否则refs变量可能会消失。因此,您需要在其中一个指向该节点的列表上获取锁定 - 通过保持该锁定,您知道该节点无法完全删除 - 有人指的是该节点。

但是你不能在不增加引用计数的情况下获取节点列表。鸡肉和鸡蛋。 即你是某种方式" on"节点C,并希望使用节点D.首先锁定节点C中的列表,然后可以安全地遍历到D并增加D的引用计数。但是你是怎么得到的?#34;首先是C?

嗯,显然你是从A开始的。或者"头和#34;这指向A.即你需要某个地方开始,某个地方要站在"站立"。也许是一个永远不会消失的默认节点。

无论如何,假设您可以到达节点N,并且N指向M,则到达M

  • 锁定N.links
  • incr M.refs(lock refs; inc refs; unlock refs)
  • 解锁N.links
  • (即使N-to-M现在取消关联,你也会增加M,所以它不会消​​失)
  • 转到M

其他操作也是类似的。

当然,这假设一个单独的线程可以处于图形的一部分上,该图形实际上在同一时间的其他地方被断开"。即一旦你到达M,并使用其数据或其他任何东西,它现在可能会与图表断开连接(但不会因为你的引用而删除)。这应该没问题,因为"同一时间"在线程中没什么意义。