Interlocked.Increment'ed int 필드를 올바르게 읽는 방법은 무엇입니까?
비 휘발성 int 필드와 스레드가 있다고 가정 Interlocked.Increment
합니다. 다른 스레드가 이것을 직접 안전하게 읽을 수 있습니까? 아니면 읽기도 연동되어야합니까?
이전에는 필드가 휘발성이 아니기 때문에 현재 값을 확인하기 위해 연동 읽기를 사용해야한다고 생각했습니다. 나는 그것을 Interlocked.CompareExchange(int, 0, 0)
달성하기 위해 사용하고 있습니다.
그러나 실제로 일반 읽기는 항상 ed 값 의 현재 버전을 볼 수 있으며 int 읽기는 이미 원자 적이므로 특별한 작업을 할 필요가 없음 을 시사하는 이 답변 을 우연히 발견했습니다 Interlocked.Increment
. 또한 Microsoft가 Interlocked.Read (ref int) 요청을 거부하는 요청을 발견 하여 이것이 완전히 중복됨을 시사합니다.
그렇다면 이러한 int
필드 의 최신 값을 안전하게 읽을 수 Interlocked
있습니까?
다른 스레드가 최신 값을 읽도록 보장하려면을 사용해야합니다 Thread.VolatileRead()
. (*)
읽기 작업 자체는 원자 적이므로 문제가 발생하지 않지만 휘발성 읽기가 없으면 캐시에서 오래된 값을 얻거나 컴파일러가 코드를 최적화하고 읽기 작업을 완전히 제거 할 수 있습니다. 컴파일러의 관점에서는 코드가 단일 스레드 환경에서 작동하는 것으로 충분합니다. 휘발성 작업 및 메모리 장벽은 코드를 최적화하고 재정렬하는 컴파일러의 기능을 제한하는 데 사용됩니다.
코드를 변경할 수있는 참가자는 컴파일러, JIT- 컴파일러 및 CPU입니다. 그중 어느 하나가 코드가 손상되었음을 나타내는 것은 실제로 중요하지 않습니다. 유일한 중요한 것은 모든 참가자가 준수해야하는 규칙을 지정하기 때문에 .NET 메모리 모델 입니다.
(*) Thread.VolatileRead()
는 실제로 최신 값을 얻지 못합니다. 값을 읽고 읽은 후에 메모리 장벽을 추가합니다. 첫 번째 휘발성 읽기는 캐시 된 값을 얻을 수 있지만 두 번째는 필요한 경우 첫 번째 휘발성 읽기의 메모리 배리어가 캐시 업데이트를 강제했기 때문에 업데이트 된 값을 얻습니다. 실제로이 세부 사항은 코드를 작성할 때 거의 중요하지 않습니다.
약간의 메타 문제이지만 Interlocked.CompareExchange(ref value, 0, 0)
(읽기에 사용할 때 이해하기 어렵다는 명백한 단점을 무시하고) 사용에 대한 좋은 측면 은 int
또는 long
. 그것은 그 사실 int
항상 원자이다 읽지 만 long
이 아니거나 아키텍처에 따라하지 읽어야 할 수도 있습니다. 불행히도이 유형의 Interlocked.Read(ref value)
경우에만 작동합니다 .value
long
int
를 사용할 수 없게 만드는 필드로 시작하는 경우를 고려하여 Interlocked.Read()
어쨌든 원자 적이므로 대신 값을 직접 읽습니다. 그러나 나중에 개발 단계에서 사용자 나 다른 사람이 a long
가 필요하다고 결정합니다. 컴파일러는 경고하지 않지만 이제는 미묘한 버그가있을 수 있습니다. 읽기 액세스는 더 이상 원 자성이 보장되지 않습니다. Interlocked.CompareExchange()
여기에서 최상의 대안을 사용 하는 것을 발견 했습니다. 기본 프로세서 명령에 따라 느려질 수 있지만 장기적으로는 더 안전합니다. 나는 내부에 대해 충분히 알지 못합니다 Thread.VolatileRead()
. 더 많은 서명을 제공하므로이 사용 사례와 관련하여 "더 나을"수 있습니다.
루프 또는 엄격한 메서드 호출 내에서 값을 직접 (즉, 위의 메커니즘없이) 읽지 않으려 고합니다. 쓰기가 휘발성 및 / 또는 메모리 장벽이 있더라도 컴파일러에게 필드의 값은 실제로 두 읽기 사이에서 변경 될 수 있습니다 . 따라서 필드는 volatile
주어진 구문 중 하나 이거나 사용해야합니다.
내 2 센트.
32 비트 정수를 원자 적으로 읽는 데 특별한 명령이 필요하지 않다는 것이 맞습니다. 그러나 이것이 의미하는 바는 "전체"값을 얻게된다는 것입니다 (즉 , 한 쓰기의 일부와 다른 일부를 얻지 못함 ). 일단 읽은 값이 변경되지 않을 것이라는 보장은 없습니다.
이 시점에서 액세스를 제어하기 위해 다른 동기화 방법을 사용해야하는지 여부를 결정해야합니다 (예 :이 값을 사용하여 배열에서 멤버를 읽는 경우).
간단히 말해서 원자 성은 작업이 완전하고 불가분하게 발생하도록합니다. 단계 A
가 포함 된 일부 작업 이 주어 졌을 때 바로 작업 을 N
수행하면 A
모든 N
단계가 동시 작업과 분리되어 발생 했음을 확신 할 수 있습니다 .
원자 연산을 실행 한 두 개의 스레드가있는 경우 두 스레드 중 하나의 전체 결과 A
만 볼 수 있습니다 . 스레드를 조정하려는 경우 원자 적 작업을 사용하여 필요한 동기화를 만들 수 있습니다. 그러나 원자 적 작업 자체는 더 높은 수준의 동기화를 제공하지 않습니다. 방법의 가족은 몇 가지 근본적인 원자 작업을 제공하기 위해 사용할 수 있습니다.Interlocked
동기화 는 종종 원자 적 작업을 중심으로 구축되는 광범위한 동시성 제어 입니다. 대부분의 프로세서에는 모든 캐시 라인이 플러시되고 일관된 메모리보기 를 보장하는 메모리 장벽이 있습니다 . 휘발성 읽기는 주어진 메모리 위치에 대한 일관된 액세스를 보장하는 방법입니다.
문제에 즉시 적용 할 수는 없지만 데이터베이스와 관련하여 ACID (원 자성, 일관성, 격리 및 내구성)를 읽으면 용어에 도움이 될 수 있습니다.
예, 읽은 모든 것이 정확합니다. Interlocked.Increment는 필드를 변경하는 동안 일반 읽기가 거짓이되지 않도록 설계되었습니다. 필드를 읽는 것은 위험하지 않습니다. 필드를 쓰는 것은 위험합니다.
'programing' 카테고리의 다른 글
대화 상자에서 값을 반환하는 Android 'Best Practice' (0) | 2021.01.16 |
---|---|
Javascript에서 내보내기 및 프로토 타입이란 무엇입니까? (0) | 2021.01.16 |
ASP.NET MVC 아키텍처 : 구성, 상속 또는 복제에 의한 ViewModel? (0) | 2021.01.16 |
SQL Server Management Studio를 사용하여 Azure Virtual Machine에서 호스팅되는 SQL Server Express 인스턴스에 원격으로 연결 (0) | 2021.01.16 |
IOHIDEventQueue.c의 DP5 오류 (0) | 2021.01.16 |