假设我正在编写一个多线程服务器。服务器维护节点的有向图,其中每个节点可以指向并由许多其他节点指向。客户端可以添加新节点,在节点之间添加新指针,也可以删除它们。引用计数用于垃圾收集。如果两个客户端尝试执行可能导致竞争条件的操作,则应阻止一个客户端。阻塞频率应在客户端之间和节点之间保持一致。
如何使用互斥原语来完成此操作并避免竞争和死锁?我似乎无法创建一个简单的全局通行权排名,因为这有利于某些客户或节点。
如果它改变了答案,那么它是使用POSIX线程接口在C中实现的。这是节点定义的一种方式:
struct node {
pthread_mutex_t mut;
int refs; /* reference counting */
void *content; /* immutable */
struct node *links[];
}
我尝试使用递归互斥锁来锁定需要操作的每个节点(假设客户端将节点链接到自身),但我不确定这是否正确。
答案 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
其他操作也是类似的。
当然,这假设一个单独的线程可以处于图形的一部分上,该图形实际上在同一时间的其他地方被断开"。即一旦你到达M,并使用其数据或其他任何东西,它现在可能会与图表断开连接(但不会因为你的引用而删除)。这应该没问题,因为"同一时间"在线程中没什么意义。