2012-11-25 6 views
1

가능한 중복 :
Interlocked.CompareExchange<Int> using GreaterThan or LessThan instead of equality동등하지 않은 경우 Interlocked.CompareExchange?

내가 Interlocked.CompareExchange이 값과 간 비교가 동일한 경우에만 값을 교환하는 것을 알고, 그들이 동일하지 않은 경우
그들을 교환하는 방법 이런 식으로 뭔가를 얻을 수 있을까요?

if (Interlocked.CompareExchange(ref count, count + 1, max) != max) 
    // i want it to increment as long as it is not equal to max 
     { 
      //count should equal to count + 1 
     } 

답변

6

더 효율적 (이하 버스 잠금 및 낮은 읽기) 및 단순화 된 구현 : 당신이 정말 높은 경합이있는 경우

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); 
} 

는 여기에서 우리는 countmax 넘어 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을 순수 최적화라고 생각할 수 있습니다. 우리는 oldval0으로 초기화 할 수 있으며 여전히 정상적으로 작동합니다. Volatile.Read을 사용하면 하나의 CAS 만 수행 할 수 있습니다 (지시 사항대로 다중 CPU 구성에서 특히 비용이 많이 듭니다).

그러나 예, 마크가 말했듯이 잠금 장치는 단순합니다.

+0

'~'와 함께 무엇입니까? –

+0

첫 번째 패스에서 'val! = oldval'을 보장하기위한 것입니다. –

+0

아, 충분합니다. –

4

하지만, "동일하지 않을 경우 비교"이되지 않습니다 : 먼저 값 자신을 테스트 한 다음 스레드 경쟁을하지 않는 경우에만 업데이트를 할 수있다; 두 번째 테스트가 실패하면 반복해야 할 수도 있습니다. 의사 코드 :

bool retry; 
do { 
    retry = false; 
    // get current value 
    var val = Interlocked.CompareExchange(ref field, 0, 0); 
    if(val != max) { // if not maxed 
     // increment; if the value isn't what it was above: redo from start 
     retry = Interlocked.CompareExchange(ref field, val + 1, val) != val; 
    }   
} while (retry); 

하지만 솔직히 말해서 잠금 장치가 더 간단합니다. 마크는 무엇을 게시의

+0

+1 : "잠금이 더 간단합니다" –

+0

Interlocked.CompareExchange (ref 필드, 0, 0) 대신 휘발성은 어떨까요? – user1748906

+0

@ user1748906 '휘발성'은 '연동'과 동일한 격리/향기 규칙을 존중할 것입니다. 아마 작동 할거야,하지만 ... 내가 말했듯이, 당신이 기대하는 충돌이 얼마나 많은가에 달려있다. 솔직히 자물쇠는 많은 경우에 더 간단해질 것이다. –

관련 문제