Здесь довольно много вопросов о Interlocked
против volatile
здесь, на SO, я понимаю и знаю концепции 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, но не нашел однозначного ответа (куча разных вопросов и комментариев / ответов на них все говорят противоречивые вещи).