Переупорядочивание операций вокруг volatile

В настоящее время я просматриваю копию -в реализации набора для записи -и хочу подтвердить, что она потокобезопасна. Я почти уверен, что единственный способ, которым это может быть не так, - это если компилятору разрешено переупорядочивать операторы в определенных методах. Например, метод Removeвыглядит как:

public bool Remove(T item)
{
    var newHashSet = new HashSet(hashSet);
    var removed = newHashSet.Remove(item);
    hashSet = newHashSet;
    return removed;
}

Где hashSet определяется как

private volatile HashSet hashSet;

Итак, мой вопрос: учитывая, что hashSet равен volatile, означает ли это, что Removeв новом наборе происходит до записи в переменную-член? Если нет, то другие потоки могут увидеть набор до того, как произойдет удаление.

На самом деле я не видел никаких проблем с этим в производстве, но я просто хочу подтвердить, что это гарантированно безопасно.

ОБНОВЛЕНИЕ

Чтобы быть более конкретным, есть еще один способ получитьIEnumerator:

public IEnumerator GetEnumerator()
{
    return hashSet.GetEnumerator();
}

Итак, более конкретный вопрос: :есть ли гарантия, что возвращенный IEnumeratorникогда не вызовет ConcurrentModificationExceptionиз удаления?

ОБНОВЛЕНИЕ 2

Извините, все ответы касаются безопасности потоков от нескольких авторов. Хорошие очки подняты,но это не то, что я пытаюсь выяснить здесь. Я хотел бы знать, разрешено ли компилятору -упорядочивать операции в Removeпримерно так:

    var newHashSet = new HashSet(hashSet);
    hashSet = newHashSet;                  // swapped
    var removed = newHashSet.Remove(item); // swapped
    return removed;

Если бы это было возможно, это означало бы, что поток мог вызвать GetEnumeratorпосле того, как hashSetбыло назначено, но до того, как itemбыло удалено, что могло привести к изменению коллекции во время перечисления.

У Джо Даффи есть статья в блоге , в которой говорится:

Volatile on loads means ACQUIRE, no more, no less. (There are additional compiler optimization restrictions, of course, like not allowing hoisting outside of loops, but let’s focus on the MM aspects for now.) The standard definition of ACQUIRE is that subsequent memory operations may not move before the ACQUIRE instruction; e.g. given { ld.acq X, ld Y }, the ld Y cannot occur before ld.acq X. However, previous memory operations can certainly move after it; e.g. given { ld X, ld.acq Y }, the ld.acq Y can indeed occur before the ld X. The only processor Microsoft.NET code currently runs on for which this actually occurs is IA64, but this is a notable area where CLR’s MM is weaker than most machines. Next, all stores on.NET are RELEASE (regardless of volatile, i.e. volatile is a no-op in terms of jitted code). The standard definition of RELEASE is that previous memory operations may not move after a RELEASE operation; e.g. given { st X, st.rel Y }, the st.rel Y cannot occur before st X. However, subsequent memory operations can indeed move before it; e.g. given { st.rel X, ld Y }, the ld Y can move before st.rel X.

Насколько я понимаю, для вызова newHashSet.Removeтребуется ld newHashSet, а для записи в hashSetтребуется st.rel newHashSet. Из приведенного выше определения RELEASE никакие грузы не могут перемещаться после RELEASE сохранения, поэтому операторы не могут быть переупорядочены ! Может ли кто-нибудь подтвердить, пожалуйста, подтвердите, что моя интерпретация верна?

8
задан SimonC 11 July 2012 в 02:51
поделиться