代码之家  ›  专栏  ›  技术社区  ›  Dan Olson

有没有一种方法可以让两个读原子?

  •  6
  • Dan Olson  · 技术社区  · 15 年前

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

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

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

    那么,我如何使这个操作成为原子操作呢?我知道所有关于CA的内容,但它往往只涉及使读-修改-写操作成为原子操作,而这并不是我想在这里做的。

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

    编辑:谢谢,我没有提到我想在第一次修订中不加锁地做这个,但是有人在我第二次修订后就开始了。我知道没人会相信这样的人,但实际上我不能用锁。我必须用原子模拟一个互斥体,这比重构代码来跟踪一个值而不是两个值要工作得多。

    目前,我的研究方法包括利用值是连续的这一事实,并通过64位读取原子地获取这些值,我确信这些值在我的目标平台上是原子的。如果有人有新想法,请贡献!谢谢。

    4 回复  |  直到 15 年前
        1
  •  3
  •   Eddie    15 年前

    如果你真的需要确保 a b 在进行此测试时不要更改,然后需要使用相同的同步 全部的 访问 . 这是你唯一的选择。对这些值的每次读和写都需要使用相同的内存边界、同步器、信号量、时间片锁或使用的任何机制。

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

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

    然后 阅读时不会改变 . 但同样,您必须使用相同的同步机制 全部的 访问 并且 .

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

    假设这些在内存中是连续的,每个32位,并且您的处理器具有原子64位读取,那么您可以发出原子64位读取来读取这两个值,解析64位值中的两个值,进行数学运算并返回您想要返回的值。假设您不需要原子更新” 同时“但仅原子更新到” “或” “在隔离状态下,这样就可以在没有锁的情况下执行您想要的操作。

        2
  •  3
  •   Mitch Wheat    15 年前

    您必须确保这两个值中的任何一个被读取或写入,它们都被内存屏障(锁或关键部分)包围。

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

    // all writes...
    lock(lockProtectingAllAccessToMemoryOneAndTwo)
    {
        *MemoryLocationOne = someValue;
        *MemoryLocationTwo = someOtherValue;
    }
    
        3
  •  3
  •   Michael    15 年前

    如果您的目标是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;
    
        4
  •  1
  •   Zifre    15 年前

    没有锁是不可能做到这一点的。据我所知,没有处理器具有双原子读。