1

У меня есть следующее определение проблемы:Блокировка свободный список удалить операцию

Дизайн замок свободной простой связанный список со следующими операциями:

  • Добавить (пункт): добавить узел в начало (руководитель) списка
  • Remove (пункт): удалить данный пункт из списка

Ниже показан код реализован до сих пор:

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

На REMOVE части есть два случая:

  1. Узел имеет следующие not- null следующий указатель: затем выполните операцию CAS и украдите данные о значении следующего элемента, удалив фактически следующий элемент.
  2. Проблемный случай, когда элемент для удаления является последним в списке:
    • Do Atomically: If (item == oldItem и item.Next == null) then item = null где oldItem - указатель на удаляемый элемент;

Так что я хочу сделать, это в случае удаления C узла:

  • если (C == старого C-ссылки и C .next = = null), то C = null => all atomically

Проблема в том, что у меня есть CAS только на одном объекте. Как я могу решить эту проблему атомарно? Или есть лучший способ сделать эту операцию удаления, которую я упускаю здесь?

+0

Вы уверены, что вещь _sentinel работает? С типами значений невозможно отличить значение по умолчанию (T) и потенциально полезное значение. Другими словами, система типа .NET не предоставляет вам значение, которое можно использовать как часовое. – usr

+0

Действительно, значение часового может не работать для типов значений, но для простоты использования давайте просто определим предопределенный маркер как дозорный. Если вы знаете лучшее решение, пожалуйста, разделите его (https://gist.github.com/). Однако моя основная проблема заключается в операции удаления. –

+0

Ваше удаление не имеет для меня никакого смысла. Если вы хотите удалить B, вам нужно CAS A.next to C. Справа? Ваш код этого не делает. Я признаю, что это первый раз, когда я работаю над атомарным списком. – usr

ответ

1

при удалении B мы делаем трюк путем копирования содержимого Си к B и C: удаление B.Next = C.Next (в цикле) и B.Value = C.Value после того, как движение удалось

Таким образом, вам необходимо атомарно изменить два расположения памяти. CAS в .NET не поддерживает это. Вы можете, однако, обернуть эти два значения в другом объекте, который может быть выгружен атомарно:

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 имеет ту же структуру, что и ваш старый класс узла. В некотором смысле вы теперь управляете двумя физическими узлами связанного списка для каждого логического.

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

не будет работать, поскольку он будет проверять только ссылку, а не значения внутри. Я мог бы свободно изменять значение node.Next, а CAS с другим узлом (или другой ссылкой на этот узел) будет успешным, поскольку они имеют одну и ту же ссылку, даже если значение node.Next изменилось. –

+0

CAS для ссылочных типов проверяет местоположение памяти, на которое ссылается ссылка, но я заинтересован в проверке значения. –

+0

Поля ValuePlusNext должны никогда не меняться. Если вы хотите изменить их, создайте новый ValuePlusNext и CAS на месте. Таким образом, вы можете безопасно проверять и сравнивать поля. Это работает? Затем вы можете создать обновление CAS при условии, что ссылка объекта на объект ValuePlusNext не изменилась в то же время. Я добавил код. – usr

Смежные вопросы