2010-06-24 6 views
9

뮤텍스에 의해 보호되지 않는 변수 (아마도 휘발성)가 다중으로 액세스되는 (일반적으로 휘발성) 변수에 대해 다중으로 액세스되는 일반적으로 준수하는 표준 (ISO C 또는 C++ 또는 POSIX/SUS 사양) 에 할당되면 스레드는 결국 일관성있게됩니다.C/C++에서 휘발성 변수는 결국 스레드간에 일관된 의미를 갖도록 보장됩니까?

특정 예제를 제공하려면 변수 v를 공유하는 두 개의 스레드를 초기 값 0으로 간주하십시오.

스레드 1 : V = 1 개

스레드 2 동안 (V == 0) 수율();

스레드 2가 결국 종료된다는 보장이 있습니까? 또는 은 캐시 일관성이 결코 을 발동하지 않고 스레드 2의 캐시에 표시되도록하기 때문에 영원히 스핀 할 수 있습니까?

C 및 C++ 표준 (C++ 0x 이전)은 스레드 또는 동시성에 대해 에서 말하지 않습니다. 하지만 C++ 0x 메모리 모델이나 pthreads 또는 다른 어떤 것이라도 이것을 보장한다면 궁금합니다. (분명히 이것은 32 비트 x86에서 Windows에서 실제로 작동하지만, 일반적으로 의존 할 수있는 것이거나 그냥 거기에서 작동하는 경우 궁금합니다.)

+0

캐시 일관성은 CPU에서 구현되며 ** 항상 ** 주력 아키텍처에서 실행됩니다. 그것은 소프트웨어가 가지고있는 것이 아닙니다. 캐시에 내용이 기록되면 메모리에 기록되고 다른 모든 스레드는이를 볼 수 있습니다. 그건 스레딩 ​​문제가 아닙니다. 문제는 메모리 쓰기 *가 전혀 일어나지 않는지 여부입니다. 예상 시간에 발생하면 – jalf

+0

인텔 아키텍처에서 작동합니다. 나는 그것이 작동하지 않을 것이라는 소문을 들었지만 나는 직접 본적이 없다. – Omnifarious

+0

ARM은 (예를 들어) 일관된 캐시가없는 멀티 코어 아키텍처를 설계했습니다. 그 디자인이 실제로 얼마나 많이 사용되는지 잘 모릅니다. 장점은 모든 것을 유지하는 데 사용되는 실리콘과 열을 절약하는 것입니다. 물론 단점은 인텔 스레딩 모델에 익숙한 사람들의 혼란을 혼란스럽게합니다. –

답변

10

아키텍처에 따라 달라질 수 있습니다. 명시 적 캐시 플러시 또는 메모리 동기화를 통해 다른 스레드에서 메모리 쓰기를 볼 수있게하는 것은 드문 일이지만 아무 것도 배제 할 수는 없으며 분명히 플랫폼 (PowerPC 기반 장치를 포함하여 현재 개발 중임)을 접하게되었습니다. 상태가 플러시되도록 보장해야합니다.

뮤텍스와 같은 스레드 동기화 프리미티브는 필요에 따라 필요한 작업을 수행하지만 실제로 동기화가 필요하지 않으면 상태가 일관성에 대해 신경 쓰지 않고 볼 수 있도록하려는 경우 스레드 동기화 프리미티브가 실제로 필요하지 않습니다./플러시 지시로 충분합니다.

편집 : volatile 키워드에 대한 confustion 여전히 사람에게 - volatile 명시 적으로 레지스터의 데이터를 캐시 코드를 생성하지 않습니다 컴파일러를 보장하지만,이 하지 투명하게 읽기/재주문을 캐시 하드웨어 처리와 같은 일이 씁니다. 예 : this 또는 this 또는 this Dr Dobbs 문서 또는 this 질문에 대한 답변 또는 셀과 같이 약하게 일관된 메모리 아키텍처를 대상으로하는 좋아하는 컴파일러를 선택하고 테스트 코드를 작성한 다음 필요한 것만 비교하십시오 쓰기가 다른 프로세스에서 볼 수 있도록하기 위해.

+0

컴파일러는 'volatile'변수에 대한 모든 쓰기가 실제로 메인 메모리를 차지하도록 보장하기 위해 suite에 맞는 것을 수행해야하며, 그 결과 다른 스레드가 볼 수있게됩니다. –

+12

@ David : 이것은 잘못된 것입니다. "휘발성 객체에 대한 액세스는 언어 표준에 정의 된 추상 기계에 따라 엄격하게 평가되어야합니다." 이것은 C++이 어떤 종류의 최적화를 수행 할 수 있는지에 대한 설명이지 프로그래머가 아키텍처상의 단점을 처리하기 위해 할 수있는 추가 처리가 아닙니다. 컴파일러는 소스의 각 과제에 대해 명시적인 쓰기 명령어를 생성해야하지만, '플러시'또는 '동기화'또는 'eieio'또는 CPU가 실제로 데이터를 히트시킬 필요가있는 모든 것을 생성하지는 않습니다 프로그램 순서의 메모리 또는 전혀. – moonshadow

+0

'휘발성'에 대한 더 많은 진술이 있습니다. 중요한 것은 읽기 및 쓰기가 관찰 가능한 부작용이라는 것입니다. 특히, 질문 **의 루프는 v를 반복해서 읽어야합니다. 값을 캐시 할 수 없습니다. 레지스터가 아닌, L1 캐시가 아니라 다른 어떤 것도 없습니다. – MSalters

3

먼저 휘발성으로 표시되지 않으면 컴파일러에서 한 번만로드 할 수 있습니다. 따라서 메모리가 결국 변경되는지 여부에 관계없이 컴파일이 설정한다는 보장이 없습니다.

명시 적으로 "mutex 없음"이라고 말하면 pthread는 적용되지 않습니다.

그 외에도 C++에는 메모리 모델이 없으므로 하드웨어 아키텍처에 따라 다릅니다.

+1

yield() 함수가 v의 값을 변경할 수 있다고 컴파일러에서 가정해야하므로 컴파일러가 본문을 볼 수없고 변수가 컴파일 단위에 로컬이 아닌 경우 yield() @ moonshadow의 대답은 여전히 ​​적용됩니다. – CesarB

+0

N.B. 이 대답은 더 이상 적합하지 않습니다. C++은 오늘날 대부분의 컴파일러가 따르는 메모리 모델을 가지고 있습니다 (C++ 03 코드에서도 동일한 모델을 사용합니다.이 모델은 우리가 가지고있는 유일한 이식 가능하고 잘 정의 된 모델이기 때문입니다). C++ 11 이전에도 컴파일러는 그 의미에 동의하지 않았기 때문에 '휘발성'에 의존하는 것은 불가능했습니다. C++ 11 메모리 모델 이전에 올바른 다중 쓰레드 C++ 코드를 작성하려면 원자 적 연산에 플랫폼 고유의 내장 함수를 사용해야하며 '휘발성'에 의존하지 않아야합니다. –

0

나는 그것이 어떤 플랫폼에서든 작동 할 것이라고 생각하지만 지연에 대해서는 알지 못합니다.

솔직히 말해서, 이벤트에 대한 폴링 대기를 수행하는 것은 정말 안 좋은 스타일입니다. yield을 수행하더라도 아무 것도하지 않고 반복해서 프로세스가 다시 조정됩니다.

두 가지 모두에서 액세스 할 수있는 위치에 변수를 배치하는 방법을 이미 알고 있으므로 리소스를 먹지 않는 대기 시간에 올바른 도구를 사용하지 않는 이유는 무엇입니까? pthread_mutex_tpthread_cond_t 쌍은 완벽하게 트릭을해야합니다.

+0

다른 곳에서 말했듯이, 휘발성은 작동하지 않습니다. – Blaisorblade

5

관련 부분을 올바르게 이해했다면 C++ 0X는 독립형 변수 나 휘발성 변수 (휘발성 코드는 해당 용도로 설계되지 않았습니다)에 대해서는 보증하지 않지만, 보장이 있습니다 (머리글 <atomic> 참조).

2

결국 스레드 2가 결국 종료되는 것을 보장합니까? 또는 캐시 일관성이 절대로 실행되지 않고 할당이 스레드 2의 캐시에 표시되기 때문에 영원히 스핀 할 수 있습니까?

변수가 휘발성이 아닌 경우 사용자는 어떠한 보증도하지 않습니다. Pre-C++ 0x 표준은 스레드에 관해서는 말할 것도없고 변수가 휘발성이 아니기 때문에 읽기/쓰기는 관찰 가능한 부작용으로 간주되지 않으므로 컴파일러는 속일 수 있습니다. 포스트 -C++ 0x, 그것은 경쟁 조건입니다. 명시 적으로 정의되지 않은 동작으로 명시되어 있습니다.

변수가 휘발성 인 경우 은 읽기/쓰기가 발생하고은 보장되며 컴파일러는 다른 휘발성 메모리 액세스와 관련하여 재정렬되지 않습니다. (그러나 이것은 CPU가 이러한 메모리 접근을 재정렬하지 않는다는 것을 보장합니다 - 컴파일러는 그렇지 않을 것입니다)

그러나 다른 비 - 비 순차적 메모리에 대해 재정렬되지 않는다는 보장은 없습니다 - 휘발성 액세스, 그래서 당신이 예상 한 동작을 얻을 수 없습니다. 특히 컴파일러가 안전하다고 생각하면 루프를 앞두고 "보호"하려고하는 while 루프 이후의 명령어가 일부 이동 될 수 있습니다. 그러나이 분석을 수행 할 때 현재 스레드 만보고 다른 스레드에서는 발생하지 않습니다.

아니요, 일반적으로 volatile을 사용해도 올바르게 작동하지 않을 수 있습니다. 아마도 그럴 수도 있고 아마도 일 수도 있고 종종 일 수도 있지만 항상 그런 것은 아닙니다 (또한 루프 이후에 어떤 일이 발생하는지에 따라 달라집니다). 그것은 컴파일러가 최적화와 함께 얼마나 기꺼이 갈 것인지에 달려 있습니다. 그러나 그것은 입니다.은 코드를 깨기에 충분합니다. 그러니 의지하지 마십시오. 이와 비슷한 것을 동기화하려면 메모리 장벽을 사용하십시오. 그것이 바로 그들이 원하는 것입니다. (그렇다면 더 이상 volatile이 필요하지 않습니다.)

+1

+1, 필자는 단지 하나의 말다툼 만합니다. '휘발성'은 컴파일러가 액세스를 재정렬하지 않을 것임을 보증합니다. 다른 휘발성 액세스. volatile로 표시된 변수가 주 메모리 (메모리 맵핑 된 하드웨어가 아님)에 있으면 CPU는 소스 코드가 변동성이 있는지 여부를 모르기 때문에 액세스를 다시 정렬 할 수 있습니다. CPU에 관한 한 그것은 메모리에있는 주소 일뿐입니다. 이는 휘발성 변수가 순차적 일관성에 의존 할 수 없음을 의미합니다. 나는 올바른 주문을 보장하기 위해 장벽을 추가하면 휘발성 물질이 필요 없기 때문에 완전히 동의합니다. 그래서 처음부터는 쓸모가 없습니다. –

+0

@JonathanWakely 좋은 지적입니다. 고마워요. :) – jalf

3

이것은 잠재적 인 데이터 경쟁입니다.

POSIX 스레드의 경우 UB입니다. C++과 동일합니다.

실제로 어떻게 실패 할 수 있는지 상상할 수 없습니다.