2013-02-03 6 views
3

다중 스레드 프로그램에서 일부 전역 구조를 사용합니다. 일부 구성원은 동시에 여러 스레드로 수정되고 다른 일부는 수정되지 않습니다.GCC 내장형 원자재 및 휘발성 코드

나는이 멤버를 volatile로 정의하지 않았지만 언제든지이 멤버를 읽기 및 쓰기 목적으로 사용할 때마다 __sync_fetch_and_add와 같은 기본 내장 명령을 사용합니다.

질문은이 멤버 또는 전체 struct volatile을 정의해야합니까?

나는이 builtins (lock 접두사) 때문에 컴파일러가 메모리에 액세스해야한다고 생각한다. 경쟁 조건이 아닌 다른 멤버에 대해 걱정해야한다.

나는 내 컴파일러 (gcc 4.6.2)의 어셈블리 출력을 체크 아웃하고 내 가정이 맞은 것 같습니다.

다음은 테스트 코드입니다.

int sum = 0; 

for (i=0; i<2000000000; i++) { 
    sum += i; 
} 

어셈블리 출력 (-O2 -S -masm = 인텔)

L2: 
    add edx, eax 
    inc eax 
    cmp eax, 2000000000 
    jne L2 

따라서 컴파일러 (EAX = 1, EDX = 총합) 여기서

이되는 메모리를 액세스하지 않는다 두 번째 테스트 코드. 예상대로

volatile int sum = 0; 

for (i=0; i<2000000000; i++) { 
    sum += i; 
} 

어셈블리 출력

L2: 
    mov edx, DWORD PTR [esp+28] 
    add edx, eax 
    mov DWORD PTR [esp+28], edx 
    inc eax 
    cmp eax, 2000000000 
    jne L2 

컴파일러마다 합계 메모리 액세스.

최종 코드, 내 방식.

int sum = 0; 

for (i=0; i<2000000000; i++) { 
    __sync_fetch_and_add(&sum , i); 
} 

조립품 출력.

L2: 
    lock add DWORD PTR [esp+28], eax 
    inc eax 
    cmp eax, 2000000000 
    jne L2 

이전의 edx와 같은 임시 레지스터조차도 컴파일러는 매번 메모리에 액세스했습니다.

그래서 volatile 멤버를 여러 스레드로 수정하거나 한 번에 하나의 스레드로만 수정하면 volatile을 정의하지 않습니다. 나는 안전합니까?

미리 감사드립니다.

+0

휘발성은 대부분의 경우 피하는 것이 가장 좋습니다. 멀티 스레딩과 관련이 없습니다. 명시 적 장벽을 대신 사용하십시오. –

+0

나는 그렇게 생각한다.thread1에서 루프에서 전역 변수를 사용하고 컴파일러가 메모리 액세스 대신 레지스터를 사용하면 thread2가 메모리에서이 변수를 수정할 수 있고 결과가 엉망입니다. – Kurt

+1

절대적으로 그렇지 않습니다! [이 답변보기] (http://stackoverflow.com/questions/6866206/volatile-and-createthread/6866927#6866927)를 참조하십시오. GCC의 _sync 함수는 장벽으로 작동합니다. –

답변

2

예, 안전합니다. 이 문서는 그들이 휘발성을 가져야한다고 말하지 않기 때문에 그렇게해서는 안됩니다.

* __sync * 기능은 적절하게 메모리 장벽 역할을하므로 volatile은 불필요합니다. 그리고 어쨌든 __sync * 기능 이외의 다른 것을 사용하는 것을 허용하지 않습니다 (__sync * 함수 만이 lock 접두어를 생성합니다).

참고 : __sync * 함수는 gcc 4.7에서 C++ 11 스타일 __atomic * 유형 대신 사용되지 않지만 gcc 4.6에는 아직 해당 함수가 없습니다.