Предположит, что я проектировал поточно-класс, который оборачивает внутреннюю коллекцию:Должны ли волатильные и readonly быть взаимоисключающими?
public class ThreadSafeQueue<T>
{
private readonly Queue<T> _queue = new Queue<T>();
public void Enqueue(T item)
{
lock (_queue)
{
_queue.Enqueue(item);
}
}
// ...
}
Основываясь на my other question, выше реализация глючит, так как раса опасность может возникнуть при его инициализации выполняется одновременно с его использованием:
ThreadSafeQueue<int> tsqueue = null;
Parallel.Invoke(
() => tsqueue = new ThreadSafeQueue<int>(),
() => tsqueue?.Enqueue(5));
Код, приведенный выше, является приемлемо недетерминированным: элемент может быть или не быть выставлен в очередь. Однако в рамках текущей реализации он также нарушается и может привести к непредсказуемому поведению, например, бросать IndexOutOfRangeException
, NullReferenceException
, выставлять один и тот же элемент несколько раз или застревать в бесконечном цикле. Это происходит, так как Enqueue
вызов может запустить после новый экземпляр был назначен локальной переменной tsqueue
, но перед тем инициализацию внутреннего _queue
поля завершается (или, как представляется, полный).
Per Jon Skeet:
модели памяти Java не гарантирует, что конструктор завершается до ссылка на новый объект присваивается экземпляр. Модель памяти Java претерпела переработку для версии 1.5, но после этого блокировка с двойной проверкой все еще прерывается без изменчивой переменной (, как в C#).
Эта раса опасность может быть решен путем добавления барьера памяти в конструктор:
public ThreadSafeQueue()
{
Thread.MemoryBarrier();
}
Эквивалентен, это может быть более сжато решено путем создания поля летучего:
private volatile readonly Queue<T> _queue = new Queue<T>();
Однако , последний запрещен компилятором C#:
'Program.ThreadSafeQueue<T>._queue': a field cannot be both volatile and readonly
Учитывая, что вышесказанное кажется оправданным прецедентом для volatile readonly
, является ли это недостатком в оформлении языка?
Я знаю, что можно просто удалить readonly
, так как это не влияет на открытый интерфейс класса. Тем не менее, это не относится к делу, так как это можно сказать и для readonly
в целом. Я также знаю существующий вопрос «Why readonly and volatile modifiers are mutually exclusive?»; однако, они занялись другой проблемой.
Конкретный сценарий: Эта проблема, похоже, влияет на код в пространстве имен System.Collections.Concurrent
самой библиотеки классов .NET Framework. Вложенный класс ConcurrentQueue<T>.Segment
имеет несколько полей, которые только когда-либо назначаются внутри конструктора: m_array
, m_state
, m_index
и m_source
. Из них только m_index
объявляется как только для чтения; другие не могут быть - хотя и должны - поскольку они должны быть объявлены как летучие для удовлетворения требований безопасности потоков.
private class Segment
{
internal volatile T[] m_array; // should be readonly too
internal volatile VolatileBool[] m_state; // should be readonly too
private volatile Segment m_next;
internal readonly long m_index;
private volatile int m_low;
private volatile int m_high;
private volatile ConcurrentQueue<T> m_source; // should be readonly too
internal Segment(long index, ConcurrentQueue<T> source)
{
m_array = new T[SEGMENT_SIZE]; // field only assigned here
m_state = new VolatileBool[SEGMENT_SIZE]; // field only assigned here
m_high = -1;
m_index = index; // field only assigned here
m_source = source; // field only assigned here
}
internal void Grow()
{
// m_index and m_source need to be volatile since race hazards
// may otherwise arise if this method is called before
// initialization completes (or appears to complete)
Segment newSegment = new Segment(m_index + 1, m_source);
m_next = newSegment;
m_source.m_tail = m_next;
}
// ...
}
Как этот вопрос может быть чем иным, как основанным на мнениях? Если вы не надеетесь на то, что Эрик Липперт перезвонит, я не понимаю, как кто-то еще мог ответить на это больше, чем спекуляции или мнение? –
Неразумно думать, что Эрик Липперт может перезвонить. Если он все еще помнит что-нибудь о C#;) – adv12
Мне было бы трудно увидеть такой код, написанный на самом деле в реальной жизни. Есть ли реальный правильный случай, когда это потребуется, кроме целенаправленного уродливого и недетерминированного кода? Или, если я ошибаюсь, и люди на самом деле это делают, я буду счастлив стоять исправленным. –