【问题标题】:How to make an object in c++ volatile?如何使 C++ 中的对象易失?
【发布时间】:2018-08-29 02:48:14
【问题描述】:
`struct MyClass {
  ~MyClass() {
    // Asynchronously invoke deletion (erase) of entries from my_map;
    // Different entries are deleted in different threads.
    // Need to spin as 'this' object is shared among threads and 
    // destruction of the object will result in seg faults.
    while(my_map.size() > 0); // This spins for ever due to complier optimization.
  }
  unordered_map<key, value> my_map;
};`

我有上面的类,其中无序映射的元素在析构函数中被异步删除,并且我必须旋转/睡眠,因为对象在其他线程之间共享。我不能将my_map 声明为volatile,因为它会导致编译错误。我还能在这里做什么?我如何告诉编译器my_map.size() 将在某个时间点导致 0。请不要告诉我这个设计为什么/如何不好;我无法更改设计,因为它是有约束力的,除非我在这里写数千行代码,否则我无法解释。

编辑:my_map 使用自旋锁版本进行保护。因此,线程在擦除条目之前确实会获取自旋锁。只是while(my_map.size() &gt; 0); 是我在代码中唯一的“天真”旋转。我将它转换为抓取自旋锁,然后检查大小(在一个循环中)并且它起作用了。虽然使用 condition_variable 是正确的做法,但我们使用异步编程模型(如 SEDA),它使我们使用任何休眠/yeilding 调用。

【问题讨论】:

  • 考虑使用std::mutex 来保护地图免受并发访问
  • volatile 会出现什么样的编译器错误?
  • volatile 不像现在你认为的那样。大多数编译器都会忽略该关键字,并且它不会使任何线程更安全。您寻求的模式是您想要一个 std::condition_variable 以及一个锁/互斥锁来保护对my_map 的访问。但奇怪的是my_map 的所属类是如何在其他线程仍然引用其内部成员的情况下设法破坏的——这听起来像是一个设计问题。您是否考虑过将 std::shared_ptr 用于地图实例?正如上面其他人所建议的那样,您需要一个围绕 my_map 访问的互斥锁。
  • 打败我。我唯一要添加的是指向What is the XY Problem? 的链接。
  • volatile NOT 是否意味着原子!您不能将它用于线程同步。使用std::atomic 或锁。

标签: c++ multithreading asynchronous volatile unordered-map


【解决方案1】:

volatile 不是解决这个问题的方法。 volatile 正好有三种用途:1. 访问驱动程序中的内存映射设备,2. 信号处理程序,3. setjmp 使用。

阅读以下内容,一遍又一遍,直到深入。volatile 在多线程中毫无用处。

像这样的幼稚自旋锁存在三个问题:

  1. 允许编译器缓存结果,因此您会看到“永远旋转”的行为。
  2. 在经典情况下,您有竞争条件的风险:线程A 可能检查锁变量,发现资源是可访问的,但在设置锁变量之前被抢占。随之而来的线程B 也发现了显示资源可访问的锁变量,因此它锁定它并开始访问资源,然后线程A 重新唤醒,再次锁定变量,并访问资源。
  3. 存在数据写入顺序问题。如果写入受保护变量,然后更改锁定变量,则无法保证其他线程不会看到受保护变量更改,即使它也可能看到声称已写入的锁定变量。编译器都允许在 CPU 上乱序执行。

volatile 只解决了这些问题中的第一个,它没有解决其他两个问题。需要注意的是,默认情况下,x86 / x64 上的 MSVC 会为 volatile 访问添加内存栅栏,即使标准没有要求。这恰好解决了第三个问题,但仍然无法解决第二个问题。

所有这三个问题的唯一解决方案是使用正确的同步原语:std::atomic&lt;&gt;,如果你真的必须自旋锁,最好是std::mutex,也许std::condition_variable 可以让线程休眠直到某事有趣的事情发生了。

【讨论】:

  • volatile 可以在微基准测试中也很有用,因为它会抑制优化,因此可以用来防止编译器优化掉你想要测量的其他未使用的代码。但这是一个非常小众的用例。
  • @JesperJuhl 这实际上是我使用volatile的唯一情况。
猜你喜欢
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-04
  • 2011-06-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多