2009-06-17 201 views
2

了评论What's wrong with this fix for double checked locking?说:可以在构造函数被调用之前完成赋值工作吗?

的问题是,该变量可以是 分配的构造函数运行 (或完成)之前,而不是对象 分配之前。

让我们考虑代码:

A *a; 

void Test() 
{ 
    a = new A; 
} 

,以便更正式的分析,让我们分裂A =新的A到几个操作:

void *mem = malloc(sizeof(A)); // Allocation 
new(mem) A; // Constructor 
a = reinterpret_cast<A *>(mem); // Assignment 

是上面真引述评论,如果是,从何种意义上说?作业后可以执行构造函数吗?如果可以的话,由于MT安全需要保证订单时可以采取什么措施?

+0

对不起。修复了代码。 – Suma 2009-06-17 22:00:59

+1

没有什么可以对付它,真的。你将不得不做任何读取和写入volatile,并且必须使“mem”变得不稳定,以便将代码保存在你的代码写入的顺序中,并且所有这些写入/读取都由一个序列点分开。但是这仍然不会做任何w.r.t多线程:标准不知道它。 – 2009-06-17 22:01:54

回答

1

问题不在于代码执行时,这么多的 “C++和双检锁的风险”,但更多的是与写作顺序有关。

让我们假设:

A() 
{ 
    member = 7; 
} 

再后来:

singleton = new A() 

这会导致代码不分配,内存(会员)写,然后到另一个存储位置的写入(单)。有些CPU可以重新排序写操作,直到写入单例之后才能看到写入成员 - 实质上,在系统中的其他CPU上运行的代码可以具有写入单例的内存视图,但成员是不。

1

a是静态存储期限,所以它会在一些预先分配存储的主要得到执行的主体之前的某个时候被初始化的全局对象。假设对Test的调用不是某些静态对象构造怪异的结果,则a将在调用Test时完全构造。

a = new A; 

这稍微异常分配不会是(仅)一个标准拷贝分配操作作为要分配的指针A到,而不是一个对象或参考。无论它实际上编译和究竟它调用取决于A是否有赋值运算符,需要一个指针A,或者说隐含可转换从指针到A还是A有一个非显式构造函数指针A(或指向基类A的指针)。

发布编辑,你的代码做了一些相当不同的事!

概念,它更多的东西是这样的:

A *tmpa; 
void *mem = ::operator new(sizeof(A)); // (or possibly A::operator new) 

try 
{ 
    tmpa = new (mem) A; // placement new = default constructor call 
} 
catch (...) 
{ 
    ::operator delete(mem); 
    throw; 
} 

a = tmpa; // pointer assignment won't throw. 

与写出来的东西像这样的危险是,你的隐式增添了不少的序列点,仅仅是不存在的,原来,和此外,只要执行程序可以确定,编译器就可以生成看起来不像这样的代码,只要它的行为“好像”一样。这个“as if”规则只适用于正在执行的线程,因为(当前)语言没有说明与其他线程的交互作用。

为此,您需要使用由您的实现所支持的特定行为保证(如果有的话)。

-1

是的,构造函数可以在赋值之后调用,尽管你给出的例子不是内部一致的(正如对它的评论所指出的那样)。

为了安心,你可以放一些锁,但也很容易出错。

看到

斯科特迈尔斯和安德烈Alexandrescu的

http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

1

我认为以下应工作:

void Test() 
{ 
    A *temp = new A; 
    MemoryWriteBarrier(); // use whatever memory barrier your platform offers 
    a = temp; 
} 
相关问题