Требуются ли барьеры памяти при присоединении к потоку?

Если поток A порождает другой поток B с единственной целью записи в переменную V, а затем ждет его завершения, требуются ли барьеры памяти, чтобы гарантировать, что последующие чтения V в потоке A будут свежими? Я не уверен, есть ли какие-либо неявные барьеры в операциях завершения/присоединения, которые делают их избыточными.

Вот пример:

public static T ExecuteWithCustomStackSize<T>
    (Func<T> func, int stackSize)
{
    T result = default(T);

    var thread = new Thread(
        () => 
                {
                    result = func();
                    Thread.MemoryBarrier(); // Required?
                }
        , stackSize);

    thread.Start();
    thread.Join();

    Thread.MemoryBarrier(); // Required?
    return result;
}

Требуются ли какие-либо/оба (или более) барьера в приведенном выше фрагменте?


person Ani    schedule 16.09.2012    source источник
comment
Я сомневаюсь, что требуется какой-либо барьер памяти. Если бы они были, то Thread.Join был бы довольно бесполезен, и у многих людей были бы проблемы. Присоединение ожидает завершения потока, что включает в себя присвоение значения переменной.   -  person Despertar    schedule 16.09.2012


Ответы (3)


Нет, механизмы синхронизации создают неявные барьеры памяти. Все данные, измененные потоком, будут видны после присоединения к потоку.

person Tudor    schedule 16.09.2012
comment
Спасибо за Ваш ответ. Любая документация, подтверждающая это? - person Ani; 16.09.2012
comment
@Ani: В этом источнике: albahari.com/threading/part4.aspx (который все уже знают), они упоминают почти каждый механизм синхронизации как создание забора. Они явно не упоминают Join, но поскольку он помещает вызывающий поток в то же состояние, что и Monitor.Wait, например, это сильный намек, что он также должен генерировать забор. Кроме того, также упоминается ожидание Task. Хотя присоединение к потоку немного отличается, я ожидаю, что оно обеспечит такие же гарантии упорядочения памяти. - person Tudor; 16.09.2012
comment
Я прочитал это, но не был уверен, что это убедительно, поскольку, как вы говорите, в нем явно не упоминается Join. - person Ani; 17.09.2012
comment
@Ani: Ну, я искал по всему Интернету, и все, кажется, опускают Thread.Join из таких обсуждений. Я даже пытался просмотреть какой-то старый код библиотеки .NET, выпущенный MS, но Join переходит в нативный код. Я полагаю, что единственная гарантия, которую я могу вам дать, заключается в следующем: он приостанавливает вызывающий поток, поэтому он вероятно использует событие внутри, он должен генерировать забор, иначе общая семантика соединения будет нарушена, а в Java генерирует забор. :)) - person Tudor; 19.09.2012
comment
Большое спасибо за изучение этого! - person Ani; 19.09.2012

Из документации похоже, что они не требуются -

MemoryBarrier требуется только в многопроцессорных системах со слабым порядком памяти (например, в системе, использующей несколько процессоров Intel Itanium).

В большинстве случаев инструкция блокировки C#, инструкция Visual Basic SyncLock или класс Monitor обеспечивают более простые способы синхронизации данных.

Поскольку вы блокируете соединение, это тем более не нужно.

person NiladriBose    schedule 16.09.2012

Вам не нужен первый барьер памяти. Вам нужно только вызвать их перед доступом к данным, которые были изменены в отдельном потоке. Поскольку вы не делаете этого внутри «потока», вам не нужен вызов.

Вы можете избавиться от второго, если планируете сохранить вызов Join. Если вы сохраните второй вызов, вы можете избавиться от Присоединения.

person user123    schedule 16.09.2012