최근에, 나는 모든 스레드에 의해 액세스 글로벌 배열을 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
유형의 배열이다.
문제는'seq'의 타입'uintptr_t'가 ** atomically ** (즉, 단일 명령 사이클에서) 읽혀지기 때문에'seq'를 읽는 것이 중간 값이 될 수 있다는 것입니다. . – spockwang
만약'seq'가 일치하지 않는다면, 쓰레드는 다음 원소로 계속 될 것입니다. 왜냐하면 atomic CAS 연산의 비교가 실패 할 것이기 때문입니다. 판독은 후보 슬롯을 찾고 (이는 궁극적으로는 사용 가능하지 않을 수 있음), CAS는 동기화 보장을 제공한다. – jspcal
'pthread_key_delete()'도이 트릭을 사용합니다. 유효하지 않은 키를 삭제하지 않도록 동기화 보증을 제공하는 CAS에 의존합니다. 그러나 유효하지 않은 키로 오판하여 유효한 키를 삭제하는 것을 잊을 수 있습니다. – spockwang