그의 놀라운 책 C#을 간단히 요약하면 (무료 챕터는 온라인으로 제공됩니다), 메모리 장벽을 통해 "새로 고침"가치를 얻는 방법에 대해 이야기합니다. 그의 예는 다음과 같습니다.MemoryBarrier는 실제로 새로 고침 값을 보장합니까?
static void Main()
{
bool complete = false;
var t = new Thread(() =>
{
bool toggle = false;
while (!complete)
{
toggle = !toggle;
}
});
t.Start();
Thread.Sleep(1000);
complete = true;
t.Join(); // Blocks indefinitely
}
릴리스 모드로 빌드하면 제안 된대로 무기한 차단됩니다. 그는이 블록을 해결할 수있는 몇 가지 솔루션을 제공합니다. while 루프에서는 Thread.MemoryBarrier를 사용하고, lock을 사용하거나 "complete"volatile static 필드를 사용하십시오.
휘발성 필드 솔루션에 동의하는 이유는 휘발성 메모리가 JIT 용 레지스터 읽기가 아닌 직접 메모리 읽기를 시행하기 때문입니다. 그러나 나는이 최적화가 펜스와 메모리 장벽과 아무 관련이 없다고 생각한다. JIT 최적화가 메모리 나 레지스터에서 읽는 것을 선호하는 것과 마찬가지로 JIT 최적화의 문제입니다. 실제로 대신 메모리 배리어를 사용하는 모든 메소드 호출에서와 같이 모든 레지스터를 사용하지 JIT "확신"
class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static bool Toggle(bool toggle)
{
return !toggle;
}
static void Main()
{
bool complete = false;
var t = new Thread(() =>
{
bool toggle = false;
while (!complete)
{
toggle = Toggle(toggle);
}
});
t.Start();
Thread.Sleep(1000);
complete = true;
t.Join(); // Blocks indefinitely
}
}
여기
내가 더미 토글 호출을하고있다. 생성 된 어셈블리 코드에서 JIT가 "완전한"지역 변수를 읽는 직접 메모리 액세스를 사용하고 있음을 분명히 알 수 있습니다. 따라서 필자의 가정은 인텔 CPU에서 적어도 컴파일러 최적화를 고려하면 MemoryBarrier는 "새로움"측면에서 아무런 역할을하지 않습니다. MemoryBarrier가 주문한 울타리를 주문합니다. 나는 그렇게 생각하는 것이 맞습니까? 나는 휘발성과 휘발성 필드 솔루션 동의
* 두 가지 * 요구 사항이 있습니다. 첫 번째는 변수를 레지스터에 저장하지 못하게하는 지터를 설득하는 것입니다. 꽤 쉽게 포기하고, 메서드 호출로 충분할 수 있습니다. 선언 * volatile *은 x86에서 효과적인 해커입니다. 또는 잠금 또는 연동과 같이 동기화 된 민감한 코드 조각으로부터의 자연스러운 방법. Interlocked.Equals (toggle, false)를 삽입하는 것만으로도 충분합니다. 다른 하나는 장벽에서 가져온 메모리 액세스 순서입니다. Volalite.Read/Write가 가장 효과적입니다. 지터가 휘발성 변수를 처리하도록 강요하므로 충분합니다. –
저는 여러분에게 동의합니다 : 이것은 메모리 장벽과는 아무런 관련이 없습니다. –
@HansPassant,'volatile'은'Volatile.Read' /'Volatile.Write'와 마찬가지로 좋은 접근법 중 하나입니다. 그러나 그것을 잊어 버릴 수는 없습니다. 또한,'Interlocked.Equals'는 단지 정적 인'Object.Equals' 일뿐입니다. 동기화와 관련해서는 충분하지 않습니다. – acelent