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

时间:2017-02-20 03:25:57

标签: c++ multithreading gcc compare-and-swap

我有一个大型数组(3e9元素)的数据,我在多个线程中更新它的值。我刚刚发现有竞争条件。

我认为没有必要锁定整个函数,因为元素彼此独立,data[1]data[234]的更新可以安全地同时进行。

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

我的代码如下,但似乎正在陷入僵局。

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; 

我还阅读了这篇文章(Lightweight spinlocks built from GCC atomic operations?)并在volatile

上添加了data

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

2 个答案:

答案 0 :(得分:1)

您的代码包含许多数据争用,包括oldLock = *lock和解锁位*lock ^= LOCK_MASK, 由于没有发布障碍,它无法将更新同步到其他核心。

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

  

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

如果要表示用于读取和写入访问的单独状态(未锁定,读取锁定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的文档说在大多数情况下,这些内置版本被视为完全障碍。你需要一个获取障碍,所以应该涵盖。

由于您的方法基于自旋锁,因此如果您想要长时间保持读锁,则效果不佳。在这种情况下,请考虑一种更简单的方法,为数据阵列中需要锁定的每个段使用单独的互斥锁。 如果您想为多个读者提供访问权限,请考虑使用std::shared_mutex(C ++ 17)或boost::shared_mutex

答案 1 :(得分:0)

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

也许从阅读一些Pthread tutorial开始(至少对于那里解释的概念)。

在C ++中阅读atomic operationsthread support

您可以启发式地考虑为连续1024(或其他两个幂)元素的每个段使用互斥锁。

您可以考虑使用producer-consumer方法。