繁体   English   中英

为什么在glibc的NPTL实现中访问pthread密钥的序列号不同步?

[英]Why accessing pthread keys' sequence number is not synchronized in glibc's NPTL implementation?

最近,当我研究如何在glibc中实现线程本地存储时,发现了以下代码,该代码实现了API 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是类型为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 *);
};

提前致谢。

在这种情况下,循环可以避免昂贵的锁获取。 稍后执行的原子比较和交换操作atomic_compare_and_exchange_bool_acq )将确保只有一个线程可以成功递增序列值并将键返回给调用方。 第一步中其他读取相同值的线程将继续循环,因为CAS仅对单个线程成功。

这是有效的,因为序列值在偶数(空)和奇数(已占用)之间交替。 将该值递增为奇数可防止其他线程获取该插槽。

通常,仅读取值比CAS指令要少一些周期,因此在执行CAS之前先查看一下值是有意义的。

有许多利用CAS指令实现低开销同步的免等待和无锁算法

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM