如何使C ++中的对象易失?

时间:2018-08-29 02:48:15

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

`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() > 0);是我在代码中唯一的“天真”旋转。我将其转换为抓取自旋锁,然后(循环查看)大小并正常工作。尽管使用condition_variable是正确的方法,但我们使用异步编程模型(例如SEDA),该模型将我们绑定为使用任何睡眠/回避调用。

1 个答案:

答案 0 :(得分:4)

volatile不能解决此问题。 volatile具有三种用途:1.访问驱动程序中的内存映射设备; 2.信号处理程序; 3. setjmp用法。

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

像这样的天真自旋锁有三个问题:

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

volatile仅解决了第一个问题,却没有解决其他两个问题。需要注意的是,默认情况下,x86 / x64上的MSVC在volatile访问中添加了内存隔离,即使标准不要求这样做。碰巧解决了第三个问题,但仍然不能解决第二个问题。

所有这三个问题的唯一解决方案包括使用正确的同步原语:std::atomic<>,如果您真的必须旋转锁,最好使用std::mutex,也许还需要std::condition_variable来锁住,睡觉直到发生有趣的事情的线程。