锁定空闲列表删除操作

时间:2015-10-10 15:05:14

标签: multithreading concurrency synchronization locking lock-free

我有以下问题定义:

使用以下操作设计无锁的简单链接列表:

  • 添加(项目):将节点添加到列表的开头(头部)
  • 删除(项目):从列表中删除给定项目

下面显示了到目前为止实现的代码:

public class List<T>
{
    private readonly T _sentinel;
    private readonly Node<T> _head;

    public List()
    {
        _head = new Node<T>();
        _sentinel = default(T);
    }

    public List(T item)
    {
        _head = new Node<T>(item);
        _sentinel = item;
    }

    public void Add(T item)
    {
        Node<T> node = new Node<T>(item);

        do
        {
            node.Next = _head.Next;
        }
        while (!Atomic.CAS(ref _head.Next, node.Next, node));
    }

    public void Remove(Node<T> item)
    {
        Node<T> next;
        Node<T> oldItem = item;  

        if (item.Value.Equals(_sentinel))
            return;

        item.Value = _sentinel;

        do
        {
            next = item.Next;

            if (next == null)
            {
                Atomic.CAS(ref item.Next, null, null);
                return;
            }

        } while (!Atomic.CAS(ref item.Next, next, next.Next));

        item.Value = next.Value;
    }
}

头部实际上是一个易于使用的虚拟(哨兵)节点。实际的头部实际上是_head.Next。  尝试删除列表的最后一个元素时,问题在于删除操作:

enter image description here

在删除部分有两种情况:

  1. 该节点具有以下非空的下一个指针:然后执行CAS操作并窃取下一个项目的值数据,实际删除下一个项目
  2. 有问题的情况是要删除的元素是列表中的最后一个元素:
    • 以原子方式执行: If(item == oldItem and item.Next == null)then item = null 其中oldItem是指向要删除的项的指针;
  3. 所以我想做的是删除 C 节点:

    • 如果( C == old-C-reference且 C .Next == null)则C = null =&gt;所有原子

    问题是我只在一个对象上有一个CAS。 我怎样才能原子地解决这个问题呢?或者有没有更好的方法来执行我在这里错过的删除操作?

1 个答案:

答案 0 :(得分:1)

  

当删除B时,我们通过将C&C的内容复制到B并删除C:B.Next = C.Next(在循环中)和B.Value = C.Value移动成功后做了一个技巧

因此您需要以原子方式修改两个内存位置。 .NET中的CAS不支持这一点。但是,您可以将这两个值包装在另一个可以原子换出的对象中:

class ValuePlusNext<T> {
 T Value;
 Node<T> Next;
}

class Node<T> {
 ValuePlusNext<T> Value;
}

现在,您可以在一个原子操作中写入两个值。 CAS(ref Value, new ValuePlusNext<T>(next.Value, next.Value.Next)。这样的事情。

奇怪的是,ValuePlusNext具有与旧Node类相同的结构。从某种意义上说,您现在正在为每个逻辑链路节点管理两个物理链接列表节点。

while (true) {
 var old = item.Value;
 var new = new ValuePlusNext(...);
 if (CAS(ref Value, old, new)) break;
}