多线程深度拷贝

Multi-Threaded Deep Copies

本文关键字:拷贝 深度 多线程      更新时间:2023-10-16

在作为多线程C++代码一部分的对象的构造函数中执行深层复制的最佳方法是什么?

取决于数据结构。

我猜你面临的问题(虽然你没有这么说)是潜在的锁定反转。如果要进行深度复制,那么可能会为需要复制的各种对象使用一个或多个锁。

如果可以定义一个 DAG(即偏序),其中节点是系统中的每个锁,并且您可能想要采用的每个锁组合都通过边缘连接,则可以确保锁永远不会在不同的线程中以不同的顺序获取。因此,特别是您不会获得锁定反转。一个典型的规则是将"最一般"的锁放在最后,因为这往往会最大限度地减少争用。

但是,如果您正在深度复制一大堆"WidgetBox"中的一个,每个"Widget"都包含基本上无法区分的"Widget",并且框的内容之间可能存在重叠,那么您自然就无法定义锁定顺序。此外,您必须先锁定WidgetBox(即使它是"最通用"的对象),因为如果没有该锁定,您将无法判断还需要锁定的内容。如果 Widget 具有可比性,您也许可以按顺序锁定每个控件,执行复制并释放所有内容。讨厌。

另一种方法是定义在所有 Widgets 和 WidgetBox 之间共享的单个锁。这可能会引入很多争用,在这种情况下,乐观锁定可能会改善事情,前提是不会与副本同时发生太多修改。

另一种选择可能是放宽您对副本的保证 - 而不是要求从深层结构的可识别状态制作完整副本,您可以首先锁定 WidgetBox,浅拷贝它(使用 refcount 或其他任何东西 - refcount 上的锁通常是"终极内锁",因此不是反转风险), 释放小部件框锁,然后依次复制每个小部件。如果小部件具有内部结构,请使用相同的方法来复制它们。结果可能包含一个 Widget 处于直到它从另一个线程中的 WidgetBox 中删除后才达到的状态,或者其他此类不一致,因此如果这不可接受,那么您不能使用此方法。但是,如果您在每个线程中一次只锁定一个对象,则无法获得锁定反转。

最后一个可能的"核"选项是使所有内容不可变,并始终在修改时复制。如果什么都不能修改,那么你不需要任何锁(尽管在线程之间传递引用时你仍然需要内存屏障)。

如果这些都不起作用,那么除非我忘记了什么,否则你就没有我的经验了。我推测数据库实现是许多与锁相关的聪明才智的地方,这将是寻求想法的领域。

fork()

我只是在开玩笑。 但这太有趣了,不能错过:)

我认为 onebyone 已经涵盖了您的大部分选择。 除了,也许,退后一步,看看你是否能找到一种方法来避免首先做深度复制......

我的第一个冲动(我不是专家):

锁定代码用于写入该对象的该对象。进行深层复制时,请锁定对象,执行深层复制,然后将其解锁。

或者,我在这里错过了什么?