programing

Interlocked.Increment'ed int 필드를 올바르게 읽는 방법은 무엇입니까?

randomtip 2021. 1. 16. 09:32
반응형

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)경우에만 작동합니다 .valuelong

int를 사용할 수 없게 만드는 필드로 시작하는 경우를 고려하여 Interlocked.Read()어쨌든 원자 적이므로 대신 값을 직접 읽습니다. 그러나 나중에 개발 단계에서 사용자 나 다른 사람이 a long가 필요하다고 결정합니다. 컴파일러는 경고하지 않지만 이제는 미묘한 버그가있을 수 있습니다. 읽기 액세스는 더 이상 원 자성이 보장되지 않습니다. Interlocked.CompareExchange()여기에서 최상의 대안을 사용 하는 것을 발견 했습니다. 기본 프로세서 명령에 따라 느려질 수 있지만 장기적으로는 더 안전합니다. 나는 내부에 대해 충분히 알지 못합니다 Thread.VolatileRead(). 더 많은 서명을 제공하므로이 사용 사례와 관련하여 "더 나을"수 있습니다.

루프 또는 엄격한 메서드 호출 내에서 값을 직접 (즉, 위의 메커니즘없이) 읽지 않으려 고합니다. 쓰기가 휘발성 및 / 또는 메모리 장벽이 있더라도 컴파일러에게 필드의 값은 실제로 두 읽기 사이에서 변경 될 수 있습니다 . 따라서 필드는 volatile주어진 구문 중 하나 이거나 사용해야합니다.

내 2 센트.


32 비트 정수를 원자 적으로 읽는 데 특별한 명령이 필요하지 않다는 것이 맞습니다. 그러나 이것이 의미하는 바는 "전체"값을 얻게된다는 것입니다 (즉 , 한 쓰기의 일부와 다른 일부를 얻지 못함 ). 일단 읽은 값이 변경되지 않을 것이라는 보장은 없습니다.

이 시점에서 액세스를 제어하기 위해 다른 동기화 방법을 사용해야하는지 여부를 결정해야합니다 (예 :이 값을 사용하여 배열에서 멤버를 읽는 경우).


간단히 말해서 원자 성은 작업이 완전하고 불가분하게 발생하도록합니다. 단계 A가 포함 된 일부 작업 주어 졌을 때 바로 작업 N수행하면 A모든 N단계가 동시 작업과 분리되어 발생 했음을 확신 할 수 있습니다 .

원자 연산을 실행 한 두 개의 스레드가있는 경우 두 스레드 중 하나의 전체 결과 A만 볼 수 있습니다 . 스레드를 조정하려는 경우 원자 적 작업을 사용하여 필요한 동기화를 만들 수 있습니다. 그러나 원자 적 작업 자체는 더 높은 수준의 동기화를 제공하지 않습니다. 방법의 가족은 몇 가지 근본적인 원자 작업을 제공하기 위해 사용할 수 있습니다.Interlocked

동기화 는 종종 원자 적 작업을 중심으로 구축되는 광범위한 동시성 제어 입니다. 대부분의 프로세서에는 모든 캐시 라인이 플러시되고 일관된 메모리보기 를 보장하는 메모리 장벽이 있습니다 . 휘발성 읽기는 주어진 메모리 위치에 대한 일관된 액세스를 보장하는 방법입니다.

문제에 즉시 적용 할 수는 없지만 데이터베이스와 관련하여 ACID (원 자성, 일관성, 격리 및 내구성)를 읽으면 용어에 도움이 될 수 있습니다.


예, 읽은 모든 것이 정확합니다. Interlocked.Increment는 필드를 변경하는 동안 일반 읽기가 거짓이되지 않도록 설계되었습니다. 필드를 읽는 것은 위험하지 않습니다. 필드를 쓰는 것은 위험합니다.

참조 URL : https://stackoverflow.com/questions/6139699/how-to-correctly-read-an-interlocked-incremented-int-field

반응형