【发布时间】:2017-07-05 15:24:57
【问题描述】:
这个问题最好用一些简单的代码来解释。
struct foo
{
static constexpr auto N=8;
double data[N]; // initialised at construction
int max; // index to maximum: data[max] is largest value
// if value < data[index]:
// – update data[index] = value
// - update max
void update(int index, double value)
{
if(value >= data[index])
return;
data[index] = value;
if(index==max) // max unaffected if index!=max
for(index=0; index!=N; ++index)
if(data[index] > data[max])
max = index;
}
};
现在,我想让foo::update() 线程安全,即允许来自不同线程的并发调用,其中参与的线程不能使用相同的index 调用。一种方法是向foo 添加一个互斥锁或简单的自旋锁(可以假定争用较低):
struct foo
{
static constexpr auto N=8;
std::atomic_flag lock = ATOMIC_FLAG_INIT;
double data[N];
int max;
// index is unique to each thread
// if value < data[index]:
// – update data[index] = value
// - update max
void update(int index, double value)
{
if(value >= data[index])
return;
while(lock.test_and_set(std::memory_order_acquire)); // aquire spinlock
data[index] = value;
if(index==max)
for(index=0; index!=N; ++index)
if(data[index] > data[max])
max = index;
lock.clear(std::memory_order_release); // release spinlock
}
};
但是,我怎样才能实现foo::update()无锁(您可以将data 和max 视为atomic)?
注意:这是原始帖子的更简单版本,与树结构无关。
【问题讨论】:
-
这是一个难题;无锁树本身就是一个研究课题
-
@LWimsey 我已经更改/简化了问题以避免树引起的任何并发症。
-
value = data[max=index];应该是max = index;,不是吗? -
@Tsyvarev 不在最初发布的代码中:
value也必须更新(因为它在上一行中使用过)。我将编辑帖子以避免这种情况并使其更清晰。 -
您的手动锁不安全:您通常需要 seq_cst 来获取锁,以防止关键部分中的负载在实际获取锁的存储之前可见。请记住,
acquire仅适用于测试和设置的加载部分,而不适用于存储。在 x86 上,它将编译为lock xchg或lock bts或其他东西,因此您只能在架构上看到问题,其中弱于seq_cst的原子 RMW 实际上可以在 asm 中以这种方式编译。 (这可能不是完全正确的推理,但我很确定acquire太弱了。)
标签: c++ multithreading atomic lock-free