2011-01-14 11 views
3

В коде, как показано ниже, если Proc1 и Proc2 выполняются одновременно на разных процессорах, возможно ли, чтобы ThingVal2 получил значение, отличное от 5 (например, ноль)?Требуется ли защита памяти (.net x86 или x64) при разыменовании полей?

 
Class SimpleThing 
    Public X As Integer 
    Sub New(ByVal value As Integer) 
     X = value 
    End Sub 
End Class 
Class ConcurrencyTest 
    Dim Thing1 As New SimpleThing(5) 
    Dim Thing2 As New SimpleThing(0) 
    Dim ThingRef As SimpleThing = Thing1 
    Dim ThingVal1, ThingVal2 As Integer 
    Sub Proc1() 
     Thing2.X = 5 
     Threading.Thread.MemoryBarrier() 
     ThingRef = Thing2 
    End Sub 
    Sub Proc2() 
     ThingVal1 = Thing2.X 
     ThingVal2 = ThingRef.X 
    End Sub 
End Class 

Я знаю, что в слабой модели как IA64, существует реальная возможность того, что Proc2 может видеть, как ThingRef поменяв, но не видеть поле Х Thing2, как, сделав это. Существует ли такой риск для приложения .Net, работающего на x86 или x64? Если Proc1 создал новый экземпляр SimpleThing, установите его поле X равным 5, а затем установите ThingRef, чтобы указать на него, этого было бы достаточно, чтобы избежать опасности, или было бы возможно, что новая вещь может быть выделена в строке кэша, которая был разделен с чем-то другим, к которому обратился поток Proc2?

Общая парадигма с многопоточным кодом состоит в том, чтобы построить неизменяемый объект и установить изменяемую ссылку, чтобы указать на него (возможно, с помощью Interlocked.CompareExchange). Всегда ли безопасно под x86/x64 читать неизменный тип без учета потоковой передачи или может вызвать проблемы? Если последний, что является предпочтительным способом в vb.net, чтобы гарантировать надежное поведение?

Кроме того, есть ли способ указать, что код должен выполняться таким образом, чтобы такие проблемы не могли возникнуть (например, ограничение выполнения одного ядра на чем-то вроде IA64, который иначе не мог бы гарантировать правильную работу)?

+0

Неизменяемость гарантируется только в том случае, если значение может быть прочитано за один цикл. Int и bool попадают в эту категорию. Не так уверен в длине, и определенно не в строке. – SRM

+0

@SRM: неизменный объект - это тот, который никогда не изменится после того, как ссылка станет доступной для кого-либо еще.Объект «SimpleThing» выше не является неизменным, потому что я пытаюсь точно спросить, что есть и не гарантируется в моделях памяти .net x86 и .net x64. – supercat

ответ

0

ОК, вы задали много вопросов. Я постараюсь ответить на то, что знаю.

-1. Пример вашего кода:

CLR с 2.0 заказал магазины. Это означает, что ваш ThingVal всегда будет 5 на x86/x64. Наверняка. Я не пробовал его на реальном IA64, но он должен работать также и на IA64, потому что CLR должна обеспечивать упорядоченные записи на всех платформах и должна быть достаточной для вашего простого примера.

-2. ad IA64 vs x86/x64:

x86/x64 имеет другую семантику памяти, и на IA64 такого риска не существует. Единственная возможная проблема заключается в том, что вы на самом деле используете язык более высокого уровня, и если он использует оптимизирующий компилятор (например, C++), вы ничего не можете предсказать, не зная точно, как компилятор делает свою оптимизацию. Недокументировано: VB не выполняет никаких глобальных оптимизаций и т. Д., Поэтому ваш код должен быть безопасным.

-3. ad immutable:

Если вы действительно просто прочитали его, и это действительно неизменно, это безопасно.

-4. ad single core:

Вы можете установить сродство нити. Это стандартное свойство каждого потока и определяет, на каких процессорах может работать поток. (Настройка близости нити .net напрямую изменяет близость в операционной системе.) Но это заставляет вашу программу работать медленно.

Также вы можете переключиться на C# и использовать volatile ключевое слово. Это поможет вам легче жить, так как он вносит какие-либо изменения в изменчивую переменную, которую сразу видят все процессоры, и в свою очередь решит все возможные проблемы, которые вы здесь представили. К сожалению, VB не предоставляет это ключевое слово.

+0

Я знаю, что x86 заказал магазины, но проблема здесь будет связана с зависимостью загрузки. Поле X из экземпляра2 могло быть прочитано в Proc2 до того, как был записан Instance2.X или ThingRef. Под .net, была ли какая-либо гарантия, что, когда код читает ThingRef.X, он не будет просто использовать кешированное значение из чтения экземпляра2.X? Это угловой случай, о котором я не слышал. – supercat

+0

Я знаю, что можно указать, что некоторые потоки должны запускаться только, например. ядро # 5, но это кажется чрезмерным. Что действительно необходимо, так это способ указать, что два или более потока не должны запускаться одновременно на разных процессорах и что любое время выполнения переходит от одного процессора к другому, старый процессор и новый процессор должны очищать свои кеши в этом порядке. – supercat

+0

Да, «упорядоченные магазины» гарантируют, что когда вы читаете ThingRef.X, вы либо читаете как новый ThingRef, так и новый X, или старый ThingRef и старый X. Это потому, что ваш X находится внутри ThingRef, поэтому вы всегда читаете ThingRef до X. Так что это безопасно. Это может быть опасно даже при наличии упорядоченных магазинов, если у вас есть X1 и X2 внутри одного и того же объекта, и они были связаны друг с другом. –