我可以将部分数据用作锁定吗?



我有一个大数组(3E9元素)数据,我正在在多个线程中更新其值。我刚刚发现有种族条件。

我认为锁定整个功能是不必要的,因为元素彼此独立,可以同时安全完成data[1]data[234]上的更新。

我还发现data[]中每个元素中最重要的位永远不会使用。在该位上实现GCC Atomic内置锁是否安全?

我的代码如下,但似乎正在发生僵局。

const unsigned short LOCK_MASK = 1<<15;
unsigned short * lock = &(data[position]); 
unsigned short oldLock, newLock;
//lock 
do {
    oldLock = *lock;
    newLock = oldLock ^ LOCK_MASK;
} while ((oldLock & LOCK_MASK) || !__sync_bool_compare_and_swap(lock, oldLock, newLock));
//update data[position] here
...
...
...
//unlock
*lock ^= LOCK_MASK; 

我还阅读了这篇文章(由GCC原子操作构建的轻质自旋锁?),并在我的data

上添加了volatile

编辑在我的设计中,0表示解锁,1表示锁定

您的代码包含许多数据竞赛,包括oldLock = *lock并解锁位*lock ^= LOCK_MASK,由于没有发布障碍,这无法使您的更新与其他内核同步。

请注意,除了锁定数组段以进行写入访问之外,您还需要锁定该段以进行读取访问,因为必须同步读取和写入。

在该位上实现GCC Atomic内置锁是否安全?

如果要表达单独的状态进行读取和写入访问(解锁,锁定的X N,写锁定),则需要多个位。锁定到2个状态的单个位限制,锁定解锁,它可以根据您的代码来实现:

const unsigned short LOCK_MASK = 1<<15;
void lock_array_segment(int position)
{
    unsigned short *lock = &data[position]; // global array
    unsigned short oldLock, newLock;
    do {
        oldLock = __atomic_load_n (lock, __ATOMIC_RELAXED);
        newLock = oldLock | LOCK_MASK; // set bit
    } while ((oldLock & LOCK_MASK) || !__sync_bool_compare_and_swap(lock, oldLock, newLock));
}

void unlock_array_segment(int position)
{
    unsigned short *lock = &data[position]; // global array
    unsigned short oldLock, newLock;
    oldLock = __atomic_load_n (lock, __ATOMIC_RELAXED);
    newLock = oldLock & ~LOCK_MASK; // clear bit
    __atomic_store_n (lock, newLock, __ATOMIC_RELEASE);
}

在大多数情况下,__sync_bool_compare_and_swap的文档说,这些内置被认为是完整的障碍。您需要在这里获得障碍,因此应涵盖。

由于您的方法是基于Spinlocking,因此如果您想将读书保留更长的时间,则无法正常工作。在这种情况下,考虑需要锁定的数据阵列中每个段的单独的segment使用单独的静音方法来考虑更直接的方法。如果要让多个阅读器访问,请考虑使用std::shared_mutex(C 17)或boost::shared_mutex

您应该考虑更多的标准锁定方式(在C 11或更高)。

也许首先要阅读一些pthread教程(至少对于那里解释的概念)。

阅读有关C 中的原子操作和线程支持。

您可以在连续的1024(或两个其他功率)元素的每个段中考虑静音。

您可能会考虑一种生产者 - 消费者的方法。

最新更新