2009-06-17 223 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; 
} 
相關問題