6

我遇到了一种情况,我需要内存中两个值的原子和。我继承的代码是这样的:

int a = *MemoryLocationOne;
memory_fence();
int b = *MemoryLocationTwo;
return (a + b) == 0;

a 和 b 的单独读取是原子的,并且代码中其他地方对这两个内存位置的所有写入也是无锁原子的。然而,问题在于这两个位置的值可以并且确实在两次读取之间发生变化。

那么如何使这个操作原子化呢?我对 CAS 了如指掌,但它往往只涉及使读-修改-写操作原子化,而这并不是我想在这里做的。

有没有办法做到这一点,或者是重构代码的最佳选择,以便我只需要检查一个值?

编辑:谢谢,我没有提到我想在第一个修订版中无锁地执行此操作,但在我第二个修订版后有人接受了它。我知道当人们说这样的话时没有人相信,但我实际上不会使用锁。我必须用原子来模拟互斥锁,这比重构代码以跟踪一个值而不是两个值要多得多。

目前,我的调查方法涉及利用值是连续的这一事实,并通过 64 位读取原子地抓取它们,我确信这在我的目标平台上是原子的。如果有人有新的想法,请贡献!谢谢。

4

4 回答 4

3

您必须确保在读取或写入两个值中的任何一个的任何地方,它们都被内存屏障(锁或临界区)包围。

// all reads...
lock(lockProtectingAllAccessToMemoryOneAndTwo)
{
    a = *MemoryLocationOne;
    b = *MemoryLocationTwo;
}

...

// all writes...
lock(lockProtectingAllAccessToMemoryOneAndTwo)
{
    *MemoryLocationOne = someValue;
    *MemoryLocationTwo = someOtherValue;
}
于 2009-04-10T00:40:28.787 回答
3

如果您确实需要确保在进行此测试ab不进行更改,那么您需要对所有访问ab. 那是你唯一的选择。对这些值中的任何一个的每次读取和每次写入都需要使用相同的内存栅栏、同步器、信号量、时间片锁或使用的任何机制。

有了这个,您可以确保如果您:

memory_fence_start();
int a = *MemoryLocationOne;
int b = *MemoryLocationTwo;
int test = (a + b) == 0;
memory_fence_stop();

return test;

然后a在您阅读时不会改变b。但同样,您必须使用相同的同步机制来访问a访问b.

为了反映稍后对您正在寻找无锁方法的问题的编辑,这完全取决于您使用的处理器以及多长时间a以及b这些内存位置是否连续并正确对齐。

假设这些在内存中是连续的并且每个 32 位,并且您的处理器具有原子 64 位读取,那么您可以发出原子 64 位读取来读取两个值,从 64 位值中解析出这两个值,做数学并返回你想要返回的东西。假设您从不需要对“同时”进行a原子b更新,而只需要对“ a”或“ b”进行原子更新,那么这将在没有锁的情况下完成您想要的操作。

于 2009-04-10T00:40:54.340 回答
3

如果您的目标是 x86,则可以使用 64 位比较/交换支持并将两个 int 打包成一个 64 位字。

在 Windows 上,您可以这样做:

// Skipping ensuring padding.
union Data
{
     struct members
     {
         int a;
         int b;
     };

     LONGLONG _64bitData;  
};

Data* data;


Data captured;

do
{
    captured = *data;
    int result = captured.members.a + captured.members.b;
} while (InterlockedCompareExchange64((LONGLONG*)&data->_64bitData,
                    captured._64BitData,
                    captured._64bitData) != captured._64BitData);

真的很丑。我建议使用锁 - 更易于维护。

编辑:更新和阅读各个部分:

data->members.a = 0;
fence();

data->members.b = 0;
fence();

int captured = data->members.a;

int captured = data->members.b;
于 2009-04-10T00:58:34.670 回答
1

没有锁真的没有办法做到这一点。据我所知,没有处理器具有双重原子读取。

于 2009-04-10T00:44:03.257 回答