Здесь, на SO, довольно много вопросов о Interlocked
и volatile
, я понимаю и знаю концепции volatile
(без переупорядочивания, всегда чтение из памяти и т. Д.), И я знаю, как работает Interlocked
в том смысле, что он выполняет атомарная операция.
Но мой вопрос таков: предположим, у меня есть поле, которое читается из нескольких потоков, это некоторый ссылочный тип, скажем: public Object MyObject;
. Я знаю, что если я произведу сравнительный обмен на нем, например: Interlocked.CompareExchange(ref MyObject, newValue, oldValue)
, который заблокирован, гарантирует запись только newValue
в то место памяти, на которое ссылается ref MyObject
, если ref MyObject
и oldValue
в настоящее время относятся к одному и тому же объекту.
А как насчет чтения? Interlocked
гарантирует, что любые потоки, читающие MyObject
после успешной CompareExchange
операции, мгновенно получат новое значение, или мне нужно пометить MyObject
как volatile
, чтобы гарантировать это?
Причина, по которой мне интересно, заключается в том, что я реализовал связанный список без блокировки, который постоянно обновляет «головной» узел внутри себя, когда вы добавляете к нему элемент, например:
[System.Diagnostics.DebuggerDisplay("Length={Length}")]
public class LinkedList<T>
{
LList<T>.Cell head;
// ....
public void Prepend(T item)
{
LList<T>.Cell oldHead;
LList<T>.Cell newHead;
do
{
oldHead = head;
newHead = LList<T>.Cons(item, oldHead);
} while (!Object.ReferenceEquals(Interlocked.CompareExchange(ref head, newHead, oldHead), oldHead));
}
// ....
}
Теперь, после Prepend
успеха, гарантированно ли потоки, читающие head
, получат последнюю версию, даже если она не помечена как volatile
?
Я проводил несколько эмпирических тестов, и, похоже, он работает нормально, и я искал здесь SO, но не нашел окончательного ответа (множество разных вопросов и комментариев / ответов в них все говорят о противоречивых вещах).
Interlocked
завершится ошибкой, еслиhead
был изменен другим потоком. Цикл будет вращаться и повторить попытку. - person Brian Gideon   schedule 06.12.2011