【问题标题】:Many reads/one write in atomic variable原子变量中的多次读取/一次写入
【发布时间】:2015-07-16 09:41:31
【问题描述】:

请你帮帮我。

假设我有 p - 1 个读线程和一个写线程。它们都在一个原子 int 变量中读取和写入。如果所有读取和写入同时发生,写入操作会等待 p - 1 次吗?我有疑问,因为当原子操作发生时,会有一些奇怪的锁(在汇编程序中),我担心它会锁定内存(变量所在的位置)。因此,写操作可能会等待 p-1 读取。会发生吗?

下面是一些简单的代码:

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>


std::atomic<int> val;

void writer()
{
    val.store(7);
}

void read()
{
    int tmp = val.load();

    while (tmp == 0)
    {
        std::cout << std::this_thread::get_id() << ": wait" << std::endl;
        tmp = val.load();
    }

    std::cout << std::this_thread::get_id() << " Operation: " << tmp * tmp << std::endl;
}

int main()
{
    val.store(0);

    std::vector<std::thread> v;

    for (int i = 0; i < 1; ++i)
        v.push_back(std::thread(read));

    std::this_thread::sleep_for(std::chrono::milliseconds(77));

    writer();

    std::for_each(v.begin(), v.end(), std::mem_fn(&std::thread::join));

    return 0;
}

谢谢!

【问题讨论】:

  • 我不知道 atomic 的真正作用,但很有可能是:1. 锁定 2. 访问 3. 解锁。所以如果你想prioritize 访问,atomic 对它来说太简单了。您应该设置更复杂的锁定结构。应该有“写请求”标志 1.写入时,在修改值之前设置它,修改值,然后清除它。 2. 读取时,在设置此标志之前不要触摸变量。结果:当读取访问到达时,它将暂停直到写入完成。
  • 但是同样的问题出现了。当我在写入时设置标志时,它可能会等待一些读取访问结束。而且我担心(写入线程的)等待时间与读取访问次数成正比。
  • 我还有一个想法:双缓冲。有两个值,A和B。有一个index变量,指向实际值,可以是A或B。读线程是从实际值中读取,由描述索引。 Write 可以解决问题:它将值写入 1 - index,然后交换,index := 1 - index。因此,在写入之前不会有任何读取“排队”。您必须使用通用锁来读取 A 和 B 以避免交换时的顺序问题(避免:一些读取 A 在锁 A 处挂起,因此到达的读取 B 以新值更快完成,因为没有人挂在锁 B 上) .一把锁,读顺序就OK了。

标签: c++ multithreading


【解决方案1】:
  1. 处理器的指令,它锁定内存总线(具有LOCK 前缀),在通常的高级语义中不使用锁定。它使线程(调用者之一,可能还有一些访问相同或附近内存块的并发线程)有点慢。 此的上限仅取决于机器及其架构。 普通锁也会使线程变慢,但这种变慢的程度高度取决于锁争用、锁实现属性(例如,公平性)和锁保护下的代码。 除非出于性能原因,否则您不应该担心锁定的内存访问

  2. 实际上,原子加载/存储不需要 LOCK 前缀。我猜,这是一个编译器优化,它提供了顺序一致的内存顺序。默认情况下,此顺序由 .store().load() atomic 的方法强制执行,但在您的示例中是不必要的。最常用的模式是:

    • 使用宽松的内存顺序进行初始化:

      val.store(0, std::memory_order_relaxed);
      
    • 使用获取内存顺序来读取值:

      tmp = val.load(std::memory_order_acquire);
      
    • 使用释放内存顺序来写入(更改)值:

      val.store(7, std::memory_order_release);
      

    这将阻止编译器使用带有 LOCK 前缀的指令。

【讨论】:

    猜你喜欢
    • 2012-07-15
    • 1970-01-01
    • 2010-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    相关资源
    最近更新 更多