2014-07-27 6 views
1

특정 스레드가 특정 메모리 위치에서 __sync_val_compare_and_swap을 사용하여 값을 원자 적으로 설정한다고 가정 해보십시오. 비교 및 스왑 조작을 거치지 않고이 메모리 위치를 직접 수정하는 또 다른 스레드입니다. 이 경우 일관성 보장은 무엇입니까 (각 스레드마다?).비교할 수없는 동시 수정과 비교 및 ​​스왑 동작

답변

0

정확한 동작은 아키텍처에 따라 다릅니다. 실행중인 프로세서를 지정하지 않았으며 __sync_val_compare_and_swap이 프로세서의 명령어 (예 : lock cmpxchg)로 구현되었거나 LL/SC 명령어를 통해 관찰 된 동작에 영향을 미칠 수 있는지 여부 (둘 다 LL 만 가져 오지만/SC는 CAS보다 더 민감하기 때문에).

또한 메모리 순서 보증을 통해 동작이 변경됩니다. ARM 또는 Power와 같은 시스템이 x86 (-64) 또는 SPARC와 다른 동작을 나타낼 수 있습니다. 이 부분을 만지지 만 프로그램의 동작에 어떤 영향을 줄 수 있는지에 대한 자세한 내용은 Samy Al Bahra's ACM Queue article "Nonblocking Algorithms and Scalable Multicore Programming"을 읽어 보는 것이 좋습니다.

또한 CAS 작업으로 정확히 무엇을하고 있는지에 대해서도 언급하지 않았습니다. 그냥 과제 야? CAS 루프가있는 잠금없는 데이터 구조를 구현하고 있습니까? 명확하지 않으며 더 나은 대답을 더 나은 질문과 함께 제공 할 수 있습니다. 논의 할 가치가있는 부분을 가능한 한 작게 만들려고합니다. 따라서 두 스레드에서 실제로 읽기 - 수정 - 쓰기 작업을 수행하고 있다고 가정 해 보겠습니다. 다음이있는 경우 :

volatile uint32_t i = 0; 
bool stop = false; 

void * 
t0(void *arg __attribute__((unused))) 
{ 
    volatile uint32_t snap; 
    while (!stop) { 
     snap = i; 
     __sync_val_compare_and_swap(&i, snap, snap + 1); 
    } 
    return NULL; 
} 

void * 
t1(void *arg __attribute__((unused))) 
{ 

    while (!stop) { 
     i++; 
    } 
    return NULL 
} 

일관성있는 내용이 없으므로 일관성 보장에 대해 이야기 할 수 없습니다. 이 프로그램은 일관성이 없습니다. 스레드 내에서 일관성 보장을 말할 수는 없지만 때로는 t0이 값을 증가시키지 못할 수도 있습니다.

이 예는 그렇게 해가 없습니다. 메모리에 저장된 숫자를 증가시키는 프로세서가 내장되어 있어도 (캐시에 없다고 가정 할 때), 메모리에서 해당 숫자를 읽고, 숫자를 증가시키고, 그 숫자를 다시 기록해야합니다. Google의 t1은 항상 값을 증가시키는 데 성공하지만 때로는 t0만큼 증가하는 값이됩니다.

왜?

증분은 CAS와 마찬가지로 읽기/수정/쓰기 연산입니다. i++inc (%rax)으로 변환되며 메모리 읽기, 증가, 메모리 쓰기 또는 직접 해당 명령으로 변환되는지 여부로 분해됩니다. 메모리가 읽고 쓰여질 때 아키텍처마다 다릅니다.

CAS와 증가가 모두 성공했을 수 있습니다. t0이 실행되는 코어는 아직 t1 님의 메모리에 대한 쓰기를 아직 보지 못했을 수 있습니다. 특히 ARM 또는 Power와 같은 편안한 메모리 주문 아키텍처의 경우 이론적으로 t0t1에 쓰여진 값보다 i 뒤에있는 값으로 업데이트 할 수 있습니다.

__sync_val_compare_and_swap은 GCC 문서에 따라 전체 메모리 장벽을 의미하지만 다른 스레드는 이러한 장벽을 제공하지 않습니다.

우리는 메모리 울타리를 추가하여이 문제를 해결할 수 : 우리가 지금 메모리 작업의 전체 순서를 보장하기 때문에이 도움이

void * 
t1(void *arg __attribute__((unused))) 
{ 

    while (!stop) { 
     i++; 
     __sync_synchronize(); 
    } 
    return NULL 
} 

인한 __sync_synchronize에 의해 배치 전체 메모리 장벽.우리는 여전히 t0t1 사이에서 경쟁 할 수 있습니다. 그렇다면 울타리가없는 것처럼 우리는 결코 하나의 증분을 잃을 것입니다. 즉, 여전히 일관성 보장이 없다는 것입니다. 이제는 두 스레드가 모두 실행되면 적어도 하나의 증가가 발생한다는 것이 보장됩니다.

일관성이 있는지 또는 올바른지 여부는 일관성 또는 올바른 정의 방법에 따라 다릅니다. 우리가 이전에 가진 것보다 "정확하고" "일관성이 있습니다".

물론 정확성을 원한다면 __sync_val_compare_and_swap을 사용하는 두 개의 스레드가 필요합니다. GCC는 메모리 장벽을 삽입하기 때문에 실제로는 대상 아키텍처에서의 메모리 순서 지정 의미에 관계없이 정확성을 보장합니다.

마지막으로, 이런 종류의 코드를 작성하는 경우에는 Concurrency Kit을 사용하는 것이 좋습니다. API는 훨씬 더 쾌적하고 포괄적이며 어디에서나 메모리 장벽을 의미하지는 않습니다 (필요하지 않을 때 성능에 긍정적 인 영향을 미칠 수 있으며 코드가있을 때 더 명시 적으로 적용 할 수 있으며 가벼운로드를 사용할 수 있습니다). - 또는 가능하면 상점 전용 울타리).