문제

필드에 값을 쓸 때, 기본 메모리에 새 값이 언제 저장 될지에 대해 어떤 보장을 받습니까? 예를 들어, 프로세서가 개인 캐시에서 새 값을 유지하지 않고 기본 메모리를 업데이트했음을 어떻게 알 수 있습니까?
또 다른 예:

int m_foo;

void Read() // executed by thread X (on processor #0)
{
   Console.Write(m_foo);
}

void Write() // executed by thread Y (on processor #1)
{
   m_foo = 1;
}

이후에 가능성이 있습니까? 쓰다() 실행이 완료되었고 다른 스레드가 실행되었습니다 읽다() 그러나 실제로 "0"이 현재 값으로 보게 될까요? (아마도 m_foo에 대한 이전의 글은 아직 플러시되지 않았기 때문에?).
글을 플러시 할 수 있도록 어떤 종류의 프리미티브 (자물쇠 옆)를 사용할 수 있습니까?


편집하다
내가 사용한 코드 샘플에서 쓰기 및 읽기는 다른 방법으로 배치됩니다. MemoryBarrier가 동일한 범위에 존재하는 명령으로 만 영향을 미치지 않습니까?

또한 JIT에 의해 감소되지 않을 것이라고 가정 해 봅시다. M_FOO에 기록 된 값이 레지스터에 저장되지 않고 메인 메모리에 저장되지 않도록하는 방법은 무엇입니까? (또는 m_foo를 읽을 때 CPU 캐시에서 오래된 값을 얻지 못합니다).

잠금 장치 나 '휘발성'키워드를 사용하지 않고이를 달성 할 수 있습니까? (또한 원시 유형을 사용하지 않고 단어 크기의 문자를 사용한다고 가정 해 봅시다. 따라서 휘발성을 적용 할 수 없습니다.)

도움이 되었습니까?

해결책

휘발성과 인터 로크가 이미 언급되었으며, 원시인을 요청했으며 목록에 추가 된 한 가지 추가 사항은 사용하는 것입니다. Thread.MemoryBarrier() 당신의 글이나 읽기 전에. 이것은 메모리 쓰기 및 읽기에 대한 재주문이 수행되지 않는다는 것을 보장합니다.

이것은 "손으로"무엇을하고 있습니다 lock, Interlocked 그리고 volatile 대부분의 시간을 자동으로 할 수 있습니다. 이것을 다른 기술을 완전히 대체 할 수는 있지만 여행하기 가장 어려운 경로 일 것입니다. MSDN은 말합니다.

"MemoryBarrier를 사용하여 올바른 멀티 스레드 프로그램을 구축하는 것은 어렵습니다. 대부분의 목적으로 C# Lock 문, Visual Basic Synclock 문 및 모니터 클래스의 메소드는 메모리 액세스를 동기화하는 더 쉽고 오류가 발생하기 쉬운 방법을 제공합니다. 권장합니다. MemoryBarrier 대신 사용한다는 것입니다. "

MemoryBarrier를 사용하는 방법

아주 좋은 예는의 구현입니다 VolatileRead 그리고 VolatileWrite, 둘 다 내부적으로 사용합니다 MemoryBarrier. 따라야 할 기본 규칙은 : 변수를 읽을 때 메모리 장벽을 놓습니다. ~ 후에 읽기. 값을 쓸 때 메모리 장벽이 와야합니다. ~ 전에 쓰기.

이것이 이것이 덜 효율적인지 의심하는 경우 lock, 잠금은 코드 블록 전후에 메모리 배리어를 배치한다는 점에서 잠금이 "전체 펜싱"보다 더 이상 아무것도 아닙니다 (잠시 동안 모니터를 무시). 이 원칙은 이것에 잘 설명되어 있습니다 알바 하리의 스레드, 잠금, 휘발성 및 메모리 장벽에 대한 우수한 결정적인 기사.

반사판에서 :

public static void VolatileWrite(ref byte address, byte value)
{
    MemoryBarrier();
    address = value;
}

public static byte VolatileRead(ref byte address)
{
    byte num = address;
    MemoryBarrier();
    return num;
}

다른 팁

신속하게 쓰여지고 주문하는지 확인하려면 다음과 같이 표시하십시오. volatile, 또는 (더 많은 고통으로) 사용 Thread.VolatileRead / Thread.VolatileWrite (매력적인 옵션이 아니며, 놓치기 쉬워서 쓸모가 없습니다).

volatile int m_foo;

그렇지 않으면 당신은 거의 아무 것도 보장하지 않습니다 (여러 스레드를 말하 자마자).

잠금을보고 싶을 수도 있습니다 (Monitor) 또는 Interlocked, 동일한 효과를 달성 할 것입니다 ~하는 한 그만큼 같은 접근 방식은 모든 액세스 (즉, 모두)에서 사용됩니다 lock, 또는 모두 Interlocked, 등).

동기화를 사용하지 않는 한, 한 프로세서에서 실행되는 스레드가 다른 프로세서에서 실행되는 다른 스레드로 변경된 변경 사항을 볼 수 없습니다. CPU 캐시 또는 CPU 레지스터에 값이 캐시 될 수 있기 때문입니다.

따라서 변수를 휘발성으로 표시해야합니다. 그것은 글을 읽는 것 사이의 '일어나기 전'실현을 만들어 낼 것입니다.

프로세서 캐시 문제가 아닙니다. 쓰기는 일반적으로 패스 스루입니다 (쓰기는 캐시와 메인 메모리 모두로 이동)이며 모든 읽기는 캐시에 액세스합니다. 그러나 길에 다른 많은 캐시가 있습니다 (프로그래밍 언어, 라이브러리, 운영 체제, I/O 버퍼 등). 컴파일러는 또한 프로세서 레지스터에 변수를 유지하고 메인 메모리에 절대 쓰지 않도록 선택할 수 있습니다 (이것이 휘발성 연산자가 설계된 것입니다. 메모리 매핑 I/O 일 때 레지스터에 값을 저장하지 않도록합니다).

여러 프로세스 또는 여러 스레드가 있고 동기화가 문제가되는 경우 사용 사례에 따라 수행 할 수있는 방법이 많이 있습니다.

단일 스레드 프로그램의 경우, 신경 쓰지 말고, 컴파일러는해야 할 일을 수행하고 읽은 내용은 작성된 내용에 액세스 할 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top