c++ - RAII能否在没有同步的情况下有效地在线程之间共享不可变对象(immutable对象)

标签 c++ garbage-collection immutability raii

在无数关于 C++ 风格的确定性销毁 (RAII) 与垃圾收集的优越性的争论中,前者的支持者经常暗示它可​​以做垃圾收集可以做的一切。然而,有一种模式在 Java 和 .NET 语言中经常使用,但我不知道在 C++ 中有什么好的模式。考虑类 [可以是 Java 或 C#;为简洁起见使用公共(public)字段

public class MaxItemIdentifier
{
  public String maxItemName = null;
  public long maxItemValue = -0x7FFFFFFFFFFFFFFF-1;
  public void checkItem(String name, long value)
  {
    if (value > maxItemValue)
    {
      maxItemName = name;
      maxItemValue = value;
    }
  }
}

在 Java 和 C# 中,上述方法可以安全地传递一个在任何线程上创建的字符串。此外,虽然该方法不是线程安全的,但即使是不正确的线程使用也不会危及内存安全; maxItemName 仍将保证为 null 或标识传递给 checkItem 的字符串之一。此外,该方法实际上不必复制(甚至查看)任何字符串的内容;它所作用的只是字符串引用。由于引用已暴露给外界的字符串对象永远不会被修改,因此对字符串的引用可以被视为与由此标识的字符序列同义,复制引用等同于复制文本。

是否有任何方法可以用 C++ 或类似的基于 RAII 的语言编写一个等效的类,无论线程使用如何,都可以保证内存安全,但在从单个线程运行时不会不必要地低效?我知道这种方法在 C++ 中可行的唯一方法是:

  1. 每当遇到“值”大于先前最大值的项目时,该方法复制字符串的内容;这比简单地复制引用要慢。此外,我不知道这在线程使用不当的情况下能否很好地维护内存安全。

  2. 让方法接收对引用计数指针的引用,并保存该类型的变量;当接收到一个大于先前最大值的值时,原子地增加接收指针的引用计数并原子地减少先前最大项名称的引用计数;如果后者产生零,则释放该名称。这种方法看起来很安全,但在许多平台上,原子递增和递减对于单线程使用而言代价过高,但对于多线程场景中的内存安全来说却是必需的。

就我个人而言,我认为一个好的语言/框架应该同时支持 RAII 和 GC,因为两者都可以非常轻松高效地处理一些对方根本无法处理的事情。不过,在 RAII 中可能会有一些我不熟悉的其他方法来处理此类事情。在单线程场景中使用 RAII 时,有什么方法可以使上述方法高效工作,但也可用于对在一个线程上创建的字符串的引用随后可能暴露给其他线程的场景?

请注意,与其他一些对象具有可预测生命周期的多线程 RAII 场景不同,它们始终在生产者线程中创建并在消费者线程中销毁(相关帖子的主题),对不可变对象(immutable对象)的引用如 字符串 经常在没有任何引用持有者被识别为“所有者”的情况下共享,并且没有任何方式知道任何特定的引用持有者是否或何时可能覆盖对字符串的最后一个幸存引用。

最佳答案

这当然是一个众所周知的问题。

如果没有一些开销,您就无法确定地销毁共享对象。在很多情况下,破坏出现在最终确定之上的原因是:

  1. 大多数对象不共享。
  2. 许多共享对象需要确定性销毁,而在最终确定之上提供该对象的成本甚至高于使用 RAII 实现引用计数的成本。

显然 RAII 在管理破坏方面做得很好。

它实际上只是在多个不同步的用户之间共享的对象,并且在最终确定胜过破坏的情况下将被放弃并且永远不会再次使用。一个例子是零拷贝多播套接字(或至少 O(1) 拷贝)。

权衡仍在确定性(有一些计算开销)和非确定性之间。因为 C 和 C++ 不强制执行单一的资源管理方法,所以实际上将基于析构函数的确定性清理与高效的非确定性清理混合在一起比在 .NET 或 Java 运行时上进行混合要容易得多,在这些运行时所有内容都经过非确定性重新分配.

原生世界中非确定性清理的一个例子是 the RCU method used in the Linux kernel .它是 C 代码,但同样适用于 C++。

因此,即使在这里,RAII 也有优势,您只需为非确定性 RCU 使用一组不同的智能指针,而不是用于本地范围确定性释放或引用计数、线程同步和确定性释放的智能指针。

真的,那就是你的想法出了问题。 RAII能够提供确定性生命周期,但不限于确定性生命周期。

关于c++ - RAII能否在没有同步的情况下有效地在线程之间共享不可变对象(immutable对象),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28521554/

相关文章:

reference - 在 Kotlin 中将 ArrayList 转换为 List 时的不变性问题

python - 具有可变和不可变属性的数据类样式对象?

perl - 使对象实例不可变

c++ - 当目标路径文件以点 ('.' 开头时,来自 boost::log 的未处理异常)

c++ - 函数返回值不变

javascript - 使用 Google Protocol Buffer 在 C++ 和 JavaScript 端点之间序列化/反序列化数据?

c++ - wcscpy_s 不影响 wchar_t*

garbage-collection - 垃圾收集/链表

java - 使用这些垃圾收集设置的 JVM 性能

Python:在交互模式下使用 gc 模块的不同行为