2017-02-21 14 views
1

제 질문을 쉽게 설명 할 수있는 방법은 코드에서 설명하는 것입니다. C에서 인과 관계된 예를 들어 답안에 관심을 쏟습니다.복잡한 C11 원자 유형 및 비 원자 읽기 최적화에 대한 부분 업데이트

// Just some complex user defined type 
typedef struct { 
    ... 
} state_t; 

typedef struct { 
    state_t states[16]; 
} state_list_t; 

static _Atomic state_list_t s_stateList; 
// For non-atomic reads 
static state_t * const s_pCurrent = &s_stateList.states[0]; 

// Called from external threads 
void get_state(state_list_t * pStateList) 
{ 
    *pStateList = atomic_load(&s_stateList); 
} 

// Only called by 'this' thread 
static void update_state(struct state_data_t const * pData) 
{ 
    state_list_t stateList = atomic_load(&s_stateList); 
    for (int i = 0; i < 16; i++) 
    { 
     // Do some updating on the data 
     do_transition(&stateList[i], pData); 
    } 
    atomic_store(&s_stateList, stateList); 
} 

// Only called by 'this' thread 
static void apply_state(state_t const * pState) 
{ 
    atomic_store(&s_stateList[0], *pState); 
} 

// Only called by this thread 
static bool check_state() 
{ 
    // Check (read) some values in the current state 
    return isOkay(s_pCurrent); 
} 

첫째, 즉 하나 개의 스레드가 읽고 처음 두 기능은 C11의 아토의 꽤 똑바로 앞으로 사용됩니다

내 구문 오류에 대한 사과, 그러나 이것은 걸쳐 요점을 파악해야한다 ...하는 다른 사람이 쓰는 가치. 내 구체적인 질문은 실제로 마지막 두 함수, apply_state 및 check_state에 관한 것이며, 실제로 이러한 것이 올바른지 여부에 달려있다.

apply_state에서 구조의 일부를 원자 적으로, 특히 배열의 첫 번째 요소 만 업데이트한다는 것을 알 수 있습니다. 본질적으로 _Atomic s_stateList의 모든 요소는 원자 (원자 번호)로 간주되므로 컴파일러는 atomic_store 호출로 문제가 없지만 다른 스레드가 '원자 적으로'읽는 동안 발생할 수 있습니다 (즉 get_state) 또는 각 호출에서 동일한 뮤텍스를 잠금/잠금 해제 과 본질적으로 동등한 동기화입니까? 나는 이것이 기본적으로 다른 변수이기 때문에 가능하다는 것을 알 수 있었다. (물론, 같은 주소이지만, 내가 상태 [1]을 사용했다면?) 그것은 다른 뮤텍스가 사용되게 할 수있다. 또한 state_t가 잠금이없는 경우 어떻게됩니까?

check_state 함수는 동일한 스레드에서만 수정되는 개체에 대해서만 읽기를 수행하기 때문에 여기에서해야 할 일은 괜찮습니다.하지만 필자는 누락 된 부분이 있는지 궁금합니다. 여기 뭐든지. 난 그냥 최근에 원자 변수에 직접 액세스 (할당 또는 함수 인수를 통해) atomic_load() 또는 atomic_store(), 그래서 비 원자 읽기에 대한 개인 참조를 유지하는 경우 궁금하네요 같은 호출을 처리하는 것으로 나타났습니다 가치있는 최적화 또는 컴파일러가 똑같은 최적화를 수행하기에 충분히 영리한 경우입니다.

편집 : 원자 값에 대한 비 원자 포인터를 역 참조 할 때 결과는 정의되지 않습니다.

+0

'static state_t * const s_pCurrent = & s_stateList [0];'제약 조건 위반입니다.포인터는 호환되지 않을뿐만 아니라 원자 유형을 가리키는 비 원자 포인터입니다. – 2501

+0

어쨌든 구문이 정확할 때 고정 된 비 원자 포인터 (다른 올바른 형식의 포인터)에 원자 값 참조를 할당하면 컴파일러로부터 불만이 없습니다. –

+0

컴파일러는 언어를 지정하지 않으므로 진실의 소스로 사용하면 안됩니다. 6.5.16.1 §1의 세 번째 단락에서 * 왼쪽에있는 을 가리키는 유형은 해당 유형의 모든 한정자가 오른쪽에 있음을 나타냅니다. * 따라서 왼쪽 포인터는 오른쪽 포인터가 수행하는 모든 한정자를 가져야합니다. – 2501

답변

2

아니요. 이는 원자재 및 좋은 이유로 C11의 모델에 맞지 않습니다. _Atomic은 구문 론적으로 한정자이며, 의미 상으로는 _Atomic이 새로운 유형입니다. 이것은 표준이 크기를 허용하고 그러한 유형의 정렬이 기준과 다른 것을 허용한다는 사실에 반영됩니다.

너비가 넓은 원자 유형의 경우 원자 유형의 허용 된 구현은 자물쇠 역할을하는 struct에 숨겨진 필드를 추가하는 것입니다. 일반적으로 이러한 유형은 데이터에 대한 액세스를 제어하는 ​​일부 숨김 상태 (별도로 struct 또는 그 안에 있음) 인 "잠금없는"상태로 구현됩니다.

표준은 액세스 모델을 연결하여 경주차를 보장 할 수 있습니다. 전체 데이터를 원자 단위로 액세스 할 수 있다고 요청하면 (즉, 전체 데이터에 대한 분할 할 수없는 연산이라는 의미에서) 모델은 사용자에게 정확하게 허용합니다.

원자 개체의 개별 필드에 액세스하면 정의되지 않은 동작이 발생합니다. 즉 플랫폼에 특정 속성이 있으면 개별 필드에 액세스 할 수 있습니다. 플랫폼의 문서를 읽고 최선을 다해, 특히 한 버전 (컴파일러, 프로세서, ...)에서 다른 버전으로 변경하지 않기를 바랍니다.

+0

Scott이 흥미로운 질문을했습니다. _Atomic이 아닌 포인터에 액세스하면, 그 포인터는 _Atomic int 유형의 객체를 가리키게됩니다. 포인터에 대한 할당 자체는 6.5.16.1 §1 때문에 캐스트가 필요하지만 표준에서는 액세스에 대해 아무 것도 말하지 않습니다. 6.5 § 7을 제외하고는 비 원자 및 원자 유형의 액세스 (앨리어싱에 관한 것)를 허용하지 않습니다. int와 _Atomic int는 호환되지 않으므로 6.5 §7에는 예외가 없습니다. 이 올바른지? (좋은 대답은 btw.) – 2501

+0

원자가 아닌 lvalue를 통해 원자에 액세스하는 것은 정의되지 않습니다. 이 작업을 수행 할 때 정확하게 잘못 될 것은 플랫폼 의존성이 많지만 이식성이 전혀 없음을 확신 할 수 있습니다. 특히 그것은 매우 드물게 발생하고 추적하기 어려운 경미한 경쟁 조건을 가질 수 있습니다. 일반적으로 C에서 캐스트는 좋은 생각이 아니며 좋은 코드는 없이도 수행 할 수 있습니다. 일반적으로 나쁜 코드는 많습니다. –

+0

답변 해 주셔서 감사합니다. 나는 사소한 (잠금없는) 케이스에서 원자력으로 일부 잠금을 대체하고보다 큰 데이터 구조를 잠금으로 대체하려고했다. 이것들은 의문의 여지가있는 문제들 중 일부였습니다. 다행이야! –