Здесь на SO задают много вопросов о Interlocked
против volatile
, я понимаю и знаю концепции volatile
(нет переупорядочивания, всегда читает из памяти и т.д.) и я знаю, как Interlocked
работает в том, что он выполняет атомарную операцию.
Но мой вопрос заключается в следующем: Предположим, у меня есть поле, которое считывается из нескольких потоков, которое является некоторым ссылочным типом, скажем: public Object MyObject;
. Я знаю, что если я сделаю сравнительный обмен по нему, например, так: Interlocked.CompareExchange(ref MyObject, newValue, oldValue)
, то interlocked гарантирует запись newValue
только в ту область памяти, на которую ссылается ref MyObject
, если ref MyObject
и oldValue
в данный момент ссылаются на один и тот же объект.
Но как насчет чтения? Гарантирует ли Interlocked
, что все потоки, читающие MyObject
после успешного завершения операции CompareExchange
, мгновенно получат новое значение, или я должен пометить MyObject
как volatile
, чтобы гарантировать это?
Причина моего вопроса в том, что я реализовал свободный от блокировок связный список, который постоянно обновляет узел "head" внутри себя, когда вы добавляете в него элемент, вот так:
[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, но не нашел окончательного ответа (куча разных вопросов и комментарии/ответы в них все говорят противоречивые вещи).