2013-03-14 2 views
3

여러 포인터를 보유하는 구조가 있습니다. 이 포인터는 여러 스레드에 의해 변경 될 수 있습니다. 이 스레드는 다른 메모리 위치를 가리 키도록 포인터를 변경하여 구조체를 업데이트합니다. 포인터는 가리키는 값을 절대로 변경하지 않습니다. 휘발성에 대한 나의 이해를 바탕으로,이 포인터를 구조체에 volatile로 선언하는 것이 합리적입니다. 업데이트 /이 포인터를 읽을이 경우 (C) '휘발성'을 올바르게 사용합니까?

스레드는 다음과 같이 작동 :

  • 복사 포인터, 우리의 복사, 우리가 갑자기 과정을 통해 반 새를 사용하지 않는 원래 변경하는 경우 그 방법을 사용합니다.
  • 복사본이 가리키는 값을 기반으로 새 포인터를 만듭니다.
  • 다른 스레드가 이미 그것을 업데이트하지 않았다면, atomic compare + swap을 사용하여 이전 포인터를 새로운 것으로 대체하십시오.

내가 타격하고 문제는 내가 포인터의 내 복사본을 만들 때, 내가 경고 얻을 수 있습니다 :

경고 : 포인터 대상 형식에서 초기화를 파기 '휘발성'규정

컴파일 된 코드는 정상적으로 작동하지만 경고로 인해 문제가 발생합니다. 휘발성을 잘못 사용하고 있습니까? 휘발성 메모리를 사용한다고 가정하면 경고를 어떻게 제거합니까? 나는 복사 포인터 선언에 휘발성 값을 매기고 싶지 않습니다. 다른 스레드는 우리 사본을 업데이트하지 않으므로 실제로 휘발성이 아닙니다. 구조체를 읽거나 쓸 때만 휘발성이 있습니다.

여기에 간단한 데모 코드가 있습니다. 실제 코드는 훨씬 좋지만 게시하기에는 너무 큽니다. 확실한 메모리 누수가 있습니다. 현재는 실제 사용 사례를 무시하고 올바르게 메모리를 추적하고 관리합니다. 나는이 문제에 대해서 '휘발성'에 대해서만 우려하고 있으며, 해결할 나의 데모에서 버그를 찾고 있지 않다.

gcc -std=gnu99 -pthread test.c && ./a.out 
test.c: In function ‘the_thread’: 
test.c:22:25: warning: initialization discards ‘volatile’ qualifier from pointer target type [enabled by default] 

코드 :

#include <stdlib.h> 
#include <stdio.h> 
#include <pthread.h> 

typedef struct { 
    volatile int *i; 
} thing; 

void init(thing *t) { 
    t->i = malloc(sizeof(int)); 
    *(t->i) = 1; 
} 

void *the_thread(void *args) { 
    thing *t = args; 

    for (int i = 0; i < 100; i++) { 
     // This generates the 'volatile' warning. But I don't want to make 
     // *copy volatile since no other threads will ever change 'copy'. 
     // Is this incorrect usage, or do I just need to do something to remove 
     // the warning? 
     int *copy = t->i; 

     int *new = malloc(sizeof(int)); 
     *new = (*copy) + 1; 

     // Glaring memory leak as old x becomes unreachable, this is a demo, 
     // the real program this is for has the issue solved. 
     // We do not care if it succeeds or fails to swap for this demo. 
     __sync_bool_compare_and_swap(&(t->i), copy, new); 
    } 
} 

int main() { 
    thing t; 
    init(&t); 

    pthread_t a; 
    pthread_t b; 

    pthread_create(&a, NULL, the_thread, &t); 
    pthread_create(&b, NULL, the_thread, &t); 

    pthread_join(a, NULL); 
    pthread_join(b, NULL); 

    return 0; 
} 

답변

3

이것은 volatile int * 때문에 "int로 휘발성 포인터"를 의미하지 않는다, 그것은 "휘발성 INT 포인터"를 의미한다. const char *a = "foo";을 상수 문자가 아닌 포인터 인 포인터와 비교하십시오.

그래서, thing 포인터는 이어야한다고 생각합니다. 필드가 "임의로"(한 스레드의 관점에서) 바뀔 수 있기 때문입니다. 당신은 (의견에서 말한 것처럼) volatile을 구조체에 int * volatile i;이되도록 이동할 수도 있습니다.

cdecl을 사용하면 이러한 문제를 즉시 해결할 수 있습니다.

+0

흠, 더 많은 조사를 해보고 실제로 구조체에 'int * volatile i'를 사용해서는 안됩니까? 그것은 내가 포인터 자체를 휘발성으로 만드는 데 사용하는 것 같다. 또한 변경하면 내 경고가 제거됩니다 ...하지만 내가 어떻게 생각하는지 작동하는지 확인하고 싶습니다 ... – Exodist

+0

할당 할 때 휘발성 int에 대한 포인터를 비 휘발성 int에 할당했기 때문에 그렇지 않은가요? 부? 사용법은 아마도 int copy = * (t-> i); – tinman

+0

@tinman 그렇다면 ints를 처리했다면 그렇습니다. 그러나 이것은 좀 더 복잡한 것에 대한 데모입니다.사실 몇 수준의 포인터가 모든 수의 스레드에 의해 변경 될 수 있으므로 구조체에 대한 포인터를 포함하는 구조체가 있으므로 포인터 자체가 휘발성이되어야합니다. – Exodist

관련 문제