最近,当我研究如何在glibc中实现线程本地存储时,我发现了以下代码,它实现了pthread_key_create()接口
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的读取不同步,如下所示:
uintptr_t seq = __pthread_keys[cnt].seq;尽管它在以后修改时是同步的。
仅供参考,__pthread_keys是一个struct pthread_key_struct类型的数组,其定义如下:
/* 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 *);
};提前谢谢。
发布于 2013-04-21 16:57:44
在这种情况下,循环可以避免昂贵的锁获取。稍后完成的原子compare and swap operation (atomic_compare_and_exchange_bool_acq)将确保只有一个线程可以成功地递增序列值并将键返回给调用者。在第一步中读取相同值的其他线程将继续循环,因为CAS只能针对单个线程成功。
这是因为序列值在偶数(空)和奇数(占用)之间交替。将该值递增为odd可防止其他线程获取该插槽。
仅读取值通常比CAS指令的周期要少,因此在执行CAS之前查看该值是有意义的。
有许多wait-free and lock-free algorithms利用CAS指令来实现低开销的同步。
https://stackoverflow.com/questions/16129336
复制相似问题