Почему бы просто не применить грубозернистый замок? Просто заблокируйте всю очередь.
Более сложное (но не обязательно более эффективное, зависит от вашего шаблона использования) решение будет использовать блокировку чтения-записи для чтения и записи соответственно.
Использование операций без блокировки кажется мне не очень хорошей идеей для вашего случая. Представьте, что какой-то поток пересекает вашу очередь, и в то же время «текущий» элемент удаляется. Не имеет значения, сколько дополнительных ссылок содержит ваш алгоритм обхода, все эти элементы могут быть удалены, поэтому у вашего кода не будет шанса завершить обход.
Другая проблема со сравнением и заменой состоит в том, что с указателями вы никогда не узнаете, действительно ли он указывает на ту же старую структуру, или старая структура была освобождена, и некоторая новая структура размещена по тому же адресу. Это может или не может быть проблемой для ваших алгоритмов.
В случае «локальной» блокировки (т. Е. Возможности заблокировать каждый элемент списка отдельно), идея состоит в том, чтобы сделать упорядоченные блокировки. Заказ замков гарантирует невозможность тупика. Итак, ваши операции таковы:
Удалить указателем p до предыдущего элемента:
- блокировка p, проверка (используя, возможно, специальный флаг в элементе), что элемент все еще находится в списке
- lock p-> next, проверьте, что это не ноль и в списке; таким образом вы гарантируете, что p-> next-> next не будет удалено за это время
- блокировка p-> next-> next
- установить флаг в p-> next, указывая, что его нет в списке
- (p-> next-> next-> prev, p-> next-> prev) = (p, null); (p-> next, p-> next-> next) = (p-> next-> next, null)
- отпустить замки
Вставить в начало:
- головка замка
- установить флаг в новом элементе, указывая, что он находится в списке
- заблокировать новый предмет
- блокировка головы-> далее
- (head-> next-> prev, new-> prev) = (new, head); (новая-> следующая, голова) = (голова, новая)
- отпустить замки
Это кажется правильным, однако я не пробовал эту идею.
По сути, это делает список с двойной связью таким, как если бы он был списком с одной ссылкой.
Если у вас нет указателя на предыдущий элемент списка (что, как правило, обычно имеет место, поскольку практически невозможно сохранить такой указатель в согласованном состоянии), вы можете сделать следующее:
Удалить по указателю c на удаляемый элемент:
- блокировка c, проверьте, является ли она все еще частью списка (это должен быть флаг в элементе списка), если нет, операция завершится неудачно
- получить указатель p = c-> prev
- разблокировать c (теперь c может быть перемещен или удален другим потоком, p также может быть перемещен или удален из списка) [чтобы избежать освобождения c, вам нужно иметь что-то вроде общего указателя или как минимум вид пересчета для элементов списка здесь]
- замок р
- проверить, является ли p частью списка (его можно удалить после шага 3); если нет, разблокируйте p и перезапустите с начала
- проверьте, равняется ли p-> next c, если нет, разблокируйте p и перезапустите с самого начала [здесь мы можем оптимизировать перезапуск, не уверен, что ATM)
- lock p-> next; здесь вы можете быть уверены, что p-> next == c и не удаляется, потому что удаление c потребовало бы блокировки p
- блокировка p-> next-> next; теперь все блокировки сняты, поэтому мы можем продолжить
- установить флаг, что c не является частью списка
- выполнить обычное (p-> next, c-> next, c-> prev, c-> next-> prev) = (c-> next, null, null, p)
- снять все блокировки
Обратите внимание, что наличие указателя на какой-либо элемент списка не может гарантировать, что элемент не был освобожден, поэтому вам необходимо выполнить своего рода пересчет, чтобы элемент не был уничтожен в тот самый момент, когда вы пытаетесь заблокировать его .
Обратите внимание, что в последнем алгоритме число повторных попыток ограничено.Действительно, новые элементы не могут появляться слева от c (вставка находится в крайнем правом положении).Если наш шаг 5 завершается неудачно и, следовательно, нам нужна повторная попытка, это может быть вызвано только тем, что p будет удалено из списка.Такое удаление может произойти не более N-1 раз, где N - начальная позиция c в списке.Конечно, этот худший случай вряд ли случится.