记忆障碍与互锁操作

时间:2010-07-21 23:57:17

标签: concurrency mutex lock-free memory-barriers

我正在努力提高对记忆障碍的理解。假设我们有一个弱内存模型,我们调整Dekker's algorithm。是否可以通过添加内存屏障使其在弱内存模型下正常工作?

我认为答案是令人惊讶的。原因(如果我是正确的)是虽然可以使用内存屏障来确保读取不会移动到另一个上,但它无法确保读取不会看到过时的数据(例如缓存中的数据)。因此,当关键部分解锁时(根据CPU的缓存),它可以看到过去的一些时间,但在当前时间,其他处理器可能会将其视为已锁定。如果我的理解是正确的,那么必须使用互锁操作,例如通常称为测试和设置或比较和交换的操作,以确保多个处理器之间在存储器位置的值的同步协议。

因此,我们能否正确地期望没有弱内存模型系统只能提供内存屏障?系统必须提供测试和设置或比较和交换等操作才有用。

我意识到流行的处理器,包括x86,提供的内存模型比弱内存模型强得多。请集中讨论弱内存模型。

(如果Dekker的算法选择不当,请选择另一种互斥算法,如果可能,内存障碍可以成功实现正确的同步。)

3 个答案:

答案 0 :(得分:5)

你是对的,内存屏障无法确保读取是否能够看到最新的值。它所做的是在单个线程和线程之间强制执行操作之间的顺序。

例如,如果线程A执行一系列存储,然后在最终存储到标志位置之前执行释放屏障,并且线程B从标志位置读取,然后在读取其他值之前执行获取屏障,则其他变量将具有由线程A存储的值:

// initially x=y=z=flag=0

// thread A
x=1;
y=2;
z=3;
release_barrier();
flag=1;

// thread B
while(flag==0) ; // loop until flag is 1
acquire_barrier();
assert(x==1);  // asserts will not fire
assert(y==2);
assert(z==3);

当然,您需要确保flag的加载和存储是原子的(只要变量适当对齐,哪些简单的加载和存储指令在大多数常见的CPU上)。如果没有线程B上的while循环,线程B可能会读取flag的陈旧值(0),因此您无法保证为其他变量读取任何值。

因此,可以使用Fences在Dekker的算法中强制执行同步。

这是C ++中的一个示例实现(使用C ++ 0x原子变量):

std::atomic<bool> flag0(false),flag1(false);
std::atomic<int> turn(0);

void p0()
{
    flag0.store(true,std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_seq_cst);

    while (flag1.load(std::memory_order_relaxed))
    {
        if (turn.load(std::memory_order_relaxed) != 0)
        {
            flag0.store(false,std::memory_order_relaxed);
            while (turn.load(std::memory_order_relaxed) != 0)
            {
            }
            flag0.store(true,std::memory_order_relaxed);
            std::atomic_thread_fence(std::memory_order_seq_cst);
        }
    }
    std::atomic_thread_fence(std::memory_order_acquire);

    // critical section


    turn.store(1,std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_release);
    flag0.store(false,std::memory_order_relaxed);
}

void p1()
{
    flag1.store(true,std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_seq_cst);

    while (flag0.load(std::memory_order_relaxed))
    {
        if (turn.load(std::memory_order_relaxed) != 1)
        {
            flag1.store(false,std::memory_order_relaxed);
            while (turn.load(std::memory_order_relaxed) != 1)
            {
            }
            flag1.store(true,std::memory_order_relaxed);
            std::atomic_thread_fence(std::memory_order_seq_cst);
        }
    }
    std::atomic_thread_fence(std::memory_order_acquire);

    // critical section


    turn.store(0,std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_release);
    flag1.store(false,std::memory_order_relaxed);
}

要进行完整分析,请参阅http://www.justsoftwaresolutions.co.uk/threading/implementing_dekkers_algorithm_with_fences.html

上的博客文章

答案 1 :(得分:1)

答案 2 :(得分:0)