我正在为一些练习阅读一本POSIX线程书,我试图在一个简单的单链表中找到我需要互斥锁的地方作为一个小练习题。例如,如果我有一个节点结构列表:
template <typename T>
struct Node
{
Node<T>* next;
T data;
};
Node<T>* head = NULL;
//Populate list starting at head...
[HEAD] --> [NEXT] --> [NEXT] --> [NEXT] --> [...] --> [NULL]
我有两个或更多线程。任何线程都可以在列表中的任何位置插入,删除或读取。
看起来如果你只是试图保护单个列表元素(而不是整个列表),你永远不能保证另一个线程没有修改下一个*指针指向的那个,所以你不能保证安全和维护不变量。
是否有更有效的方法来保护此列表,而不是使其上的所有操作使用相同的互斥锁?我原本以为有,但我真的想不到它。
另外,如果它是双重链表,情况会发生变化吗?
答案 0 :(得分:3)
如果您想使用单链表(即每个节点一个锁)进行细粒度锁定方法,那么您需要执行以下操作:
head
和tail
的列表中。这两个节点都有与之关联的锁,每个新节点都将添加到它们之间。current
指针之前,需要在下一个节点上获取锁定。在获得下一个节点上的锁之前,您也无法释放当前节点上的锁。如果您还使用prev
指针进行遍历,则将锁定该“上一个”节点,直到您将prev
指针重新分配给current
指针。prev
节点指针和current
节点指针。您首先要锁定prev
节点上的互斥锁,然后锁定current
节点上的互斥锁,并在prev
和current
节点之间添加新节点prev
和current
节点中的互斥锁(按此顺序),然后您可以删除current
节点。请记住,步骤#3和#4的工作原因是步骤#2遍历列表需要获取节点上的锁。如果跳过该步骤,则最终会创建悬空指针以及与错误指定的指针相关的其他问题,因为另一个线程会更改当前线程下方列表的拓扑。
答案 1 :(得分:2)
由于锁定单个节点不是线程安全的(一个线程可能尝试在其后插入,而另一个线程忙于删除下一个节点),因此也不会锁定节点的子集。你最好的选择是在整个列表中使用全局互斥,除非你愿意花一些时间来开发一个read-write lock
,这至少会允许多个线程同时读取列表。
由于双向链表是一个更复杂的结构,我会保持同样的建议。