더 효율적 (이하 버스 잠금 및 낮은 읽기) 및 단순화 된 구현 : 당신이 정말 높은 경합이있는 경우
static int InterlockedIncrementAndClamp(ref int count, int max)
{
int oldval = Volatile.Read(ref count), val = ~oldval;
while(oldval != max && oldval != val)
{
val = oldval;
oldval = Interlocked.CompareExchange(ref count, oldval + 1, oldval);
}
return oldval + 1;
}
, 우리는 더 일반적인 경우를 줄여 확장 성을 향상시킬 수 있습니다 단일 원자 증분 명령 : CompareExchange와 동일한 오버 헤드이지만 루프가 발생할 가능성은 없습니다.
static int InterlockedIncrementAndClamp(ref int count, int max, int drift)
{
int v = Interlocked.Increment(ref count);
while(v > (max + drift))
{
// try to adjust value.
v = Interlocked.CompareExchange(ref count, max, v);
}
return Math.Min(v, max);
}
는 여기에서 우리는
count
가
max
넘어
drift
값까지 갈 수 있습니다. 그러나 우리는 여전히
max
까지만 반환합니다. 이를 통해 대부분의 경우 op 전체를 단일 원자 증가로 변환 할 수 있으므로 확장 성이 극대화됩니다. 우리가
drift
값 이상으로 올라가면 하나 이상의 연산이 필요할뿐입니다. 매우 희귀하게 만들 수 있습니다.
volatile
그냥 일반 메모리 영업 이익이지만, 멀리 최적화되지 하나 하나 구체적으로
volatile
연동 대에 대해서는
: 연동 및 비 연동 메모리 액세스에 대한 마크의 우려에 대응
함께 작업 다른 메모리 연산과 관련하여 재정렬되지 않습니다. 이 특정 문제는 이러한 특정 속성 중 하나를 중심으로 다루지 않으므로 실제로 인터 로킹되지 않는 기능과 인터 로킹 된 기능의 상호 운용성에 대해 이야기하고 있습니다.
.NET 메모리 모델은 기본 정수 유형 (컴퓨터의 기본 단어 크기까지)의 읽기 및 쓰기를 보장하며 참조는 원 자성입니다. Interlocked 메소드는 또한 원자 적입니다. .NET에는 "원자"라는 정의가 하나뿐이므로 명시 적으로 특수한 경우에는 서로 호환 될 수 없다는 메시지가 필요하지 않습니다. 당신은 항상로드 명령을 얻을 것이다,하지만 CPU는 로컬 캐시 대신 단지 다른 CPU가 메모리에 넣어 새 값에서 이전 값을 읽을 수 있습니다
한 가지 Volatile.Read
하지 보장 가시성 않습니다. x86은 대부분의 경우 (예 : MOVNTPS
과 같은 특수 명령어) 걱정할 필요는 없지만 다른 아키텍처에서는 매우 가능성이 있습니다.요약하면
,이 영향을 미칠 수있는 두 가지 문제를 설명하는 Volatile.Read
: 첫째, 우리는 int
을 읽는 경우가 원자 될 수 없습니다 그리고 우리가 읽는 것은하지 않을 수도 있습니다, 16 비트 CPU에서 실행 될 수있는 누군가 다른 사람이 쓰는 가치. 둘째, 비록 그것이 원자 (atomic)이라 할지라도, 우리는 가시성 (visibility) 때문에 오래된 값을 읽을 수도 있습니다.
그러나 영향을받는 것은 Volatile.Read
이 알고리즘 전체에 영향을주는 것은 아니며 완벽하게 안전합니다.
~ count
을 비 원자적 방식으로 동시에 쓰는 경우 첫 번째 경우에만 우릴 것입니다. 일어날 수있는 일은 (A [0] 쓰기, CAS A [0 : 1], A [1] 쓰기) 때문입니다. count
에 대한 모든 기록은 보장 된 원자 CAS에서 발생하기 때문에 문제가되지 않습니다. 우리가 독서 중일 때 잘못된 값을 읽으면 곧 나오는 CAS에 잡힐 것입니다.
두 번째 경우는 실제로 읽기와 쓰기간에 값이 변경되는 일반적인 경우의 특수화입니다. 우리가 요청하기 전에 읽기가 발생합니다. 이 경우 첫 번째 Interlocked.CompareExchange
호출은 Volatile.Read
이 준 값과 다른 값을보고하고 성공할 때까지 루핑을 시작합니다.
원할 경우, 경합이 낮은 경우에 대한 Volatile.Read
을 순수 최적화라고 생각할 수 있습니다. 우리는 oldval
을 0
으로 초기화 할 수 있으며 여전히 정상적으로 작동합니다. Volatile.Read
을 사용하면 하나의 CAS 만 수행 할 수 있습니다 (지시 사항대로 다중 CPU 구성에서 특히 비용이 많이 듭니다).
그러나 예, 마크가 말했듯이 잠금 장치는 단순합니다.
'~'와 함께 무엇입니까? –
첫 번째 패스에서 'val! = oldval'을 보장하기위한 것입니다. –
아, 충분합니다. –