2013-12-12 2 views
41

큰 배열이 있고 여러 스레드로 내용을 처리하려고한다고 가정 해보십시오. 중복을 보장하지 않고 특정 섹션에 각 스레드를 위임하면 스레드가 배열 외부의 다른 메모리에 액세스하지 않는다고 가정 할 때 잠금이 필요하지 않습니다. 이 (의사 코드) 같은여러 스레드가 동일한 메모리에 액세스하지 못하도록함으로써 잠금을 피할 수 있습니까?

뭔가 : 실제로

global array[9000000]; 

do_something(chunk) { 
    for (i = chunk.start; i < chunk.end; i++) 
     //do something with array 
} 

main() { 
    chunk1 = {start: 0, end: 5000000}; 
    chunk2 = {start: 5000000, end: 9000000}; 

    start_thread(thread1, do_something(chunk1)); 
    start_thread(thread2, do_something(chunk2)); 

    wait_for_join(thread1); 
    wait_for_join(thread2); 
    //do something else with the altered array 
} 

답변

31

적합 C++ 11 컴파일러에서 이것은 안전하다 [intro.memory] §1.7)

메모리 위치는 스칼라 형의 개체 또는 인접한 비트 필드를 갖는 모든 비 - 제로 폭의 최대 시퀀스 중입니다. [...] 두 개의 실행 스레드 (1.10)는 서로 간섭하지 않고 별도의 메모리 위치를 업데이트하고 액세스 할 수 있습니다.

C11은 § 3.14에서 동일한 보증을 제공합니다 (동일한 문구를 사용합니다).

C++ 03 컴파일러에서는 표준에 의해 작동하지 않을 수도 있지만 컴파일러가 확장과 유사한 보증을 제공하는 경우에도 작동합니다.

+4

C++ 03에서이 동작을 제공하지 않는 컴파일러가 있습니까? 비록 그것이 공식화되지는 않았지만, 그것은 현대적인 하드웨어에서 꽤 일반적인 특징이었습니다 ... – Errata

+0

이것은 하드웨어와 아무 관련이 없습니다. src에서 dst로 15 바이트를 복사하려고한다고 가정 해 봅시다. 컴파일러는 dst에서 변경되지 않고 유지되어야하는 바이트를 읽고, 벡터 레지스터를 사용하여 16 바이트를 복사하고, 1 바이트를 다시 씁니다. 모든 스레드가 그 바이트를 보았습니다. 귀하의 소스 코드는 동일한 코드에 액세스하지 않았지만 컴파일 된 코드는 액세스했습니다. C11 및 C++ 11에서는이 작업을 수행하지 않습니다. 다른 비트 필드는 동일한 메모리로 간주 될 수 있습니다. – gnasher729

18

예 : 두 개의 스레드가 동일한 요소에 액세스하지 않는다고 보장 할 수 있다면 더 이상 동기화 할 필요가 없습니다.

두 스레드가 동기화하지 않고 두 개 이상의 스레드가 동일한 메모리 위치 (적어도 하나를 수정하는 상태)에 액세스하는 경우 충돌이 발생하므로 잠재적 인 데이터 경쟁이 발생합니다.

(참고 :이 답변은 C++ 11 메모리 모델을 기반으로합니다. 저는 또한 여러분이 제 2 언어에 대해서도 물어 봤음을 눈치 채었고, C11은 매우 유사한 메모리 모델을 지정하지만, 그 대답이 C 언어에서도 유효하다는 것을 말하지 말아라. 두 언어의 구버전에서는 thread-safety가 구현에 달려있다.)

+0

"... 두 스레드가 동기화없이 동일한 메모리 위치에 액세스하는 경우"이러한 액세스 중 하나가 쓰기입니다. –

+0

@ AndrewDurward : 실제로, 나는 약간 단순화했습니다. 이 질문에 대한 관련 요점은 그들이 같은 위치에 접근하지 않는다면 갈등이있을 수 없다는 것입니다. –

+1

캐시 라인에 '덩어리'가 겹치면 두 스레드가 서로 다른 CPU 코어의 동일한 캐시 라인에서 항목을 처리 할 때 성능이 저하 될 수 있습니다. 최상의 성능을 위해서는 청크 크기를 플랫폼의 캐시 라인 크기의 배수로 만들고 올바르게 맞 춥니 다. 그러나 상관없이 동기화가 필요 없습니다. –

4

예.

두 스레드가 액세스 같은 메모리 위치가 없음을 보장 할 필요조차 없습니다. 단 하나의 스레드가 을 수정할 필요가 없다는 점은 다른 하나가 액세스하는 모든 위치 (읽기 또는 쓰기를 의미하는지 여부는 불문 함)입니다.

을 감안할 때 중 하나또는 모든 읽기 전용 동시 액세스 없음 동시 액세스, 당신은 잠그지 않고 갈 수 있어요.

+1

단일 수정자는 보증인의 안전을 보장하기에 충분하지 않습니다. 쓰여진대로 데이터를 읽는 독자는 잘못된 결과를 얻을 수 있습니다. – ugoren

+0

@ugoren : RIght. 그래서 "** 싱글 스레드 쓰지 않기"라고했습니다. 독자는 일관성없는 결과를 얻을 수 있습니다 (모든 현대 CPU가 작동하는 방식 때문에 일관성이 없지만 대개 충분히 나쁘다). 즉, 임의의 수의 독자는 안전하지만 작성자는 없습니다 (독자와 동일한 위치에 있음). – Damon

3

중요한 성능 문제!

다른 사람들이 말한 것처럼 메모리 위치가 공유되지 않기 때문에 명시 적 잠금이 필요하지 않습니다.

임의의 청크가 캐시 라인을 공유 할 가능성이 있으므로 암시 적 하드웨어 동기화을 트리거 할 수 있습니다 (예에서 사용한 수치와 다름). false sharing으로 알려져 있습니다.

  • 덩어리 정렬 (스레드 번호) 기반 하드웨어에 따라 조정됩니다 같은 OpenMP 같은

    높은 수준의 접근 방식은 문제의이 종류를 해결합니다.

  • 스레드 풀에 대한 제어력이 뛰어납니다. 예를 들어 스레드 인스턴스화 비용을 상환해야합니다.
  • 쓰기가 쉽고 실제로 방해가 적습니다.