在作为多线程C ++代码一部分的对象的构造函数中执行深层复制的最佳方法是什么?
答案 0 :(得分:4)
取决于您的数据结构。
我猜你遇到的问题(虽然你没有这么说)是潜在的锁定倒置。如果你是深度复制,那么你可能会为你需要复制的各种对象带一个或多个锁。
如果您可以定义DAG(即部分订单),其中节点是系统中的每个锁,并且您可能想要采用的每个锁组合都通过边连接,那么您可以确保锁是从不在不同的线程中采用不同的顺序。因此,特别是你不会得到锁定反转。一个典型的规则是最后采用“最常规”锁定,因为这会使争用最小化。
但是如果你深深地复制一大堆“WidgetBox”中的一个,每个“WidgetBox”都包含基本上无法区分的“Widgets”,并且盒子内容之间可能存在重叠,那么你自然会遇到一个定义锁定顺序的问题。此外,您必须首先锁定WidgetBox(即使它是“最常见的”对象),因为如果没有锁定,您无法分辨您需要锁定的其他内容。如果窗口小部件具有可比性,您可以按顺序锁定每个窗口小部件,执行复制并释放所有内容。讨厌。
另一种方法是定义所有Widgets和WidgetBox之间共享的单个锁。这可能引入很多争用,在这种情况下,乐观锁定可能会改善一些事情,前提是不会对副本同时进行太多修改。
另一种选择可能是放宽你对副本的保证 - 而不是要求从深层结构的可识别状态制作完整副本,你可以先锁定WidgetBox,浅拷贝它(使用引用计数或其他任何东西) - refcount上的锁通常是“终极内锁”,因此不是反转风险),释放WidgetBox锁,然后依次复制每个Widgets。如果窗口小部件具有内部结构,则使用相同的方法复制窗口小部件。结果可能包含一个Widget状态,直到它从另一个线程中的WidgetBox中删除之后才会实现,或者其他此类不协调,所以如果这是不可接受的,那么你就不能使用这种方法。但是如果你在每个线程中一次只锁定一个对象,那么你就无法进行锁定反转。
最终的,可能的“核”选项,是使一切都不可变,并始终复制修改。如果没有任何东西可以修改,那么你不需要任何锁(虽然你在线程之间传递引用时仍然需要内存障碍)。
如果这些都不起作用,那么除非我忘记某些事情,否则你的经验是不合理的。我推测数据库的实现是在很多与锁相关的聪明的地方继续进行的,这将是转向想法的领域。
答案 1 :(得分:0)
fork()的
我只是在开玩笑。但是放弃它太可笑了:)
我认为onebyone涵盖了你的大部分选择。或许,可能会退后一步,看看你是否能找到一种方法来避免首先进行深层复制......
答案 2 :(得分:-1)
我的第一个冲动(我不是专家):
锁定代码用于写入的对象。当您进行深度复制时,请锁定对象,执行深层复制,然后将其解锁。
或者,我在这里错过了什么?