【发布时间】:2012-01-19 09:56:46
【问题描述】:
在下面的示例中,如果我们暂时忽略互斥体,复制省略可能会消除对复制构造函数的两次调用。
user_type foo()
{
unique_lock lock( global_mutex );
return user_type(...);
}
user_type result = foo();
现在复制省略的规则没有提到线程,但我想知道它是否真的应该跨越这样的界限。在上述情况下,逻辑抽象机器间线程中的最终副本发生在互斥锁释放之后。但是,如果省略副本,则结果数据结构会在互斥锁中初始化,因此它在互斥锁释放之前发生在线程间。
我还没有想到一个具体的例子,复制省略如何真正导致竞争条件,但内存序列中的干扰似乎是个问题。任何人都可以明确地说它不会导致问题,或者有人可以提出一个确实可以打破的例子吗?
为确保答案不仅仅针对特殊情况,请注意,如果我有类似new (&result)( foo() ) 的语句,仍然允许复制省略(根据我的阅读)。也就是说,result 不需要是堆栈对象。 user_type 本身也可以处理线程间共享的数据。
答案:我选择第一个答案作为最相关的讨论。基本上,由于标准说可能会发生省略,程序员只需要小心跨越同步边界发生。没有迹象表明这是有意还是无意的要求。我们仍然缺乏任何示例来说明可能出现的问题,因此无论哪种方式都可能不是问题。
【问题讨论】:
-
我的大脑很难将
function作为函数名来阅读,纯粹是因为其他语言。所以我的编辑可能会有点争议;不过,希望你不要介意。好问题! -
@LightnessRacesinOrbit 在您显然应该调用函数
bar的意义上存在争议? -
“我还没有想到一个具体的例子,复制省略如何真正导致竞争条件”。我也是。问题是,在没有复制省略的情况下,在析构函数的两侧都调用了复制构造函数,因此无论有没有锁,调用复制 ctor 都必须“原则上”有效。为了想出一个坏的情况,我们需要它来关心被复制到/从对象的地址是什么。例如,我们可以将
&result传递给foo,并确保~unique_lock使用result的别名,以便我们的程序始终使用锁修改result。 -
@EdA:你的意思是
new (*result)(foo())可能吗?鉴于&result,除了您所谓的“堆栈对象”(尽管自动与静态存储持续时间的实际复杂性)之外,我看不出它怎么可能是任何东西 -
我的意思不是同一个结果对象,只是全局空间某处的某个随机对象,或者从堆中分配。