简体   繁体   English

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

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

Recently when I look into how the thread-local storage is implemented in glibc, I found the following code, which implements the API pthread_key_create() 最近,当我研究如何在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 is a global array accessed by all threads. __pthread_keys是所有线程访问的全局数组。 I don't understand why the read of its member seq is not synchronized as in the following: 我不明白为什么其成员seq的读取不同步,如下所示:

uintptr_t seq = __pthread_keys[cnt].seq;

although it is syncrhonized when modified later. 尽管稍后修改它已同步。

FYI, __pthread_keys is an array of type struct pthread_key_struct , which is defined as follows: 仅供参考, __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 *);
};

Thanks in advance. 提前致谢。

In this case, the loop can avoid an expensive lock acquisition. 在这种情况下,循环可以避免昂贵的锁获取。 The atomic compare and swap operation done later ( atomic_compare_and_exchange_bool_acq ) will make sure only one thread can successfully increment the sequence value and return the key to the caller. 稍后执行的原子比较和交换操作atomic_compare_and_exchange_bool_acq )将确保只有一个线程可以成功递增序列值并将键返回给调用方。 Other threads reading the same value in the first step will keep looping since the CAS can only succeed for a single thread. 第一步中其他读取相同值的线程将继续循环,因为CAS仅对单个线程成功。

This works because the sequence value alternates between even (empty) and odd (occupied). 这是有效的,因为序列值在偶数(空)和奇数(已占用)之间交替。 Incrementing the value to odd prevents other threads from acquiring the slot. 将该值递增为奇数可防止其他线程获取该插槽。

Just reading the value is fewer cycles than the CAS instruction typically, so it makes sense to peek at the value, before doing the CAS. 通常,仅读取值比CAS指令要少一些周期,因此在执行CAS之前先查看一下值是有意义的。

There are many wait-free and lock-free algorithms that take advantage of the CAS instruction to achieve low-overhead synchronization. 有许多利用CAS指令实现低开销同步的免等待和无锁算法

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

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