Interlocked.CompareExchange는 dapper .net 메소드에서 사용되는 것입니까?

.net c# dapper thread-safety

문제

Link.TryAdd 메서드의 코드에서 다음 코드가 있습니다.

var snapshot = Interlocked.CompareExchange(ref head, null, null);

왜 단순한 것 대신에 이것이 필요한가?

var snapshot = head;

두 라인 모두 head 의 값을 변경하지 않으며 두 라인 모두 head 값을 snapshot 할당합니다. 왜 첫 번째가 두 번째로 선택 되었습니까?

편집 : 참조하는 코드는 여기에 있습니다 : https://github.com/SamSaffron/dapper-dot-net/blob/77227781c562e65c167bf7a933d69291d5bdc6f3/Dapper/SqlMapper.cs

수락 된 답변

그들은 휘발성 읽기를 원하지만 generic 형식 매개 변수를 사용하는 Thread.VolatileRead 오버로드가 없습니다. 이 방법으로 Interlocked.CompareExchange 사용하면 동일한 결과가 도출됩니다.

그들이 해결하려고하는 문제는 JIT 컴파일러가 적합하다고 판단되면 임시 할당을 최적화 할 수 있다는 것입니다. 현재 스레드가 일련의 작업에서이를 사용하는 동안 다른 스레드가 헤드 참조를 변경하면 스레드 문제가 발생할 수 있습니다.

편집하다:

TryAdd 시작 부분에 부실 값이 읽히는 것은 TryAdd . 문제는 105 번 줄에서 현재 머리를 이전 머리 ( snapshot 에서 보유)와 비교해야한다는 것입니다. 최적화가 있으면 이전 값을 보유한 snapshot 변수가 없으며 해당 시점에서 head 가 다시 읽 힙니다. head가 103과 105 사이에서 변경 되었더라도 CompareExchange 가 성공할 가능성이 매우 높습니다. 결과적으로 두 개의 스레드가 TryAdd 동시에 호출하면 목록의 노드가 손실됩니다.


인기 답변

mike z가 맞습니다. 코드를 손상시킬 수있는 JIT 최적화를 막고 있습니다.

그들은 휘발성 구조체 트릭 을 사용할 수 있었을 것이다. head 읽고 일부 struct의 휘발성 필드에 할당한다. 다음으로, 그 필드에서 읽으면 휘발성 읽기가 보장됩니다!

구조체 자체는 전혀 중요하지 않습니다. 중요한 것은 변수를 복사하는 데 휘발성 필드가 사용되었다는 것입니다.

그렇게 :

struct VolatileHelper<T> { public volatile T Value; }
...
var volatileHelper = new VolatileHelper<Field>();
volatileHelper.Value = head;
var snapshot = volatileHelper.Value;

바라기를, 그것은 런타임 비용이 없다. 어쨌든, 비용은 CPU 메모리 일관성 트래픽을 유발하는 인터 로크 된 동작보다 적습니다.

사실, 모든 캐시 액세스 (심지어 읽기 하나)가 메모리 일관성 트래픽을 필요로한다는 사실은이 캐시를 느리게 만듭니다. 연동 작업은 더 많은 CPU로 확장되지 않는 시스템 전역 리소스입니다. 연동 액세스는 전역 하드웨어 잠금 (메모리 주소 당 하나의 주소 만 있음)을 사용합니다.



아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow