2013-04-21 8 views
0

최근에, 나는 모든 스레드에 의해 액세스 글로벌 배열을 API pthread_key_create()glibc의 NPTL 구현에서 pthread 키의 시퀀스 번호에 액세스하는 것이 동기화되지 않는 이유는 무엇입니까? 내가 스레드 로컬 스토리지는 glibc에 구현되는 방식으로 볼 때

int 
__pthread_key_create (key, destr) 
     pthread_key_t *key; 
     void (*destr) (void *); 
{ 
    /* Find a slot in __pthread_kyes which is unused. */ 
    for (size_t cnt = 0; cnt < PTHREAD_KEYS_MAX; ++cnt) 
    { 
     uintptr_t seq = __pthread_keys[cnt].seq; 

     if (KEY_UNUSED (seq) && KEY_USABLE (seq) 
      /* We found an unused slot. Try to allocate it. */ 
      && ! atomic_compare_and_exchange_bool_acq (&__pthread_keys[cnt].seq, 
                 seq + 1, seq)) 
     { 
      /* Remember the destructor. */ 
      __pthread_keys[cnt].destr = destr; 

      /* Return the key to the caller. */ 
      *key = cnt; 

      /* The call succeeded. */ 
      return 0; 
     } 
    } 

    return EAGAIN; 
} 

__pthread_keys를 구현하고있는 다음 코드를 발견했다. 멤버 seq의 읽기는 다음과 같이 동기화되지 않는 이유 이해가 안 : 나중에 수정할 때이 syncrhonized 있지만

uintptr_t seq = __pthread_keys[cnt].seq; 

. 미리

/* Thread-local data handling. */ 
struct pthread_key_struct 
{ 
    /* Sequence numbers. Even numbers indicated vacant entries. Note 
     that zero is even. We use uintptr_t to not require padding on 
     32- and 64-bit machines. On 64-bit machines it helps to avoid 
     wrapping, too. */ 
    uintptr_t seq; 

    /* Destructor for the data. */ 
    void (*destr) (void *); 
}; 

감사 :

FYI, __pthread_keys 다음과 같이 정의된다 struct pthread_key_struct 유형의 배열이다.

답변

0

이 경우 루프는 값 비싼 잠금 획득을 피할 수 있습니다. compare and swap operation 나중에 완료되면 (atomic_compare_and_exchange_bool_acq) 단 하나의 스레드 만 성공적으로 시퀀스 값을 증가시키고 키를 호출자에게 리턴 할 수 있습니다. 첫 번째 단계에서 동일한 값을 읽는 다른 스레드는 CAS가 단일 스레드에서만 성공할 수 있기 때문에 루핑을 계속합니다.

이는 시퀀스 값이 짝수 (공백)와 홀수 (사용 된) 사이에서 변경되기 때문에 효과가 있습니다. 값을 홀수로 증가 시키면 다른 스레드가 슬롯을 확보하지 못합니다.

값을 읽는 것만 큼 CAS 명령보다 일반적으로 사이클 수가 적기 때문에 CAS를 수행하기 전에 값을 살짝 읽는 것이 좋습니다.

CAS 명령을 활용하여 오버 헤드가 낮은 동기화를 달성하는 데는 많은 수의 wait-free and lock-free algorithms이 있습니다.

+0

문제는'seq'의 타입'uintptr_t'가 ** atomically ** (즉, 단일 명령 사이클에서) 읽혀지기 때문에'seq'를 읽는 것이 중간 값이 될 수 있다는 것입니다. . – spockwang

+0

만약'seq'가 일치하지 않는다면, 쓰레드는 다음 원소로 계속 될 것입니다. 왜냐하면 atomic CAS 연산의 비교가 실패 할 것이기 때문입니다. 판독은 후보 슬롯을 찾고 (이는 궁극적으로는 사용 가능하지 않을 수 있음), CAS는 동기화 보장을 제공한다. – jspcal

+0

'pthread_key_delete()'도이 트릭을 사용합니다. 유효하지 않은 키를 삭제하지 않도록 동기화 보증을 제공하는 CAS에 의존합니다. 그러나 유효하지 않은 키로 오판하여 유효한 키를 삭제하는 것을 잊을 수 있습니다. – spockwang