試想一下,釋放共享指針代碼:
auto tmp = &(the_ptr->a);
*tmp = 10;
the_ptr.dec_ref();
如果dec_ref()不具有「釋放」的語義,它是一個編譯器(或CPU)從之前dec_ref搬東西完全沒有問題()後它(例如):
auto tmp = &(the_ptr->a);
the_ptr.dec_ref();
*tmp = 10;
這是不是安全的,因爲dec_ref(),也可以從其他線程在同一時間打來電話,刪除對象。 因此,它必須在dec_ref()之前有一個「釋放」語義才能留在那裏。
現在,讓我們想象對象的析構函數是這樣的:
~object() {
auto xxx = a;
printf("%i\n", xxx);
}
同時,我們會修改例如一個位,將有2個線程:
// thread 1
auto tmp = &(the_ptr->a);
*tmp = 10;
the_ptr.dec_ref();
// thread 2
the_ptr.dec_ref();
於是,「聚集的」代碼看起來例如:
// thread 1
auto tmp = &(the_ptr->a);
*tmp = 10;
{ // the_ptr.dec_ref();
if (0 == atomic_sub(...)) {
{ //~object()
auto xxx = a;
printf("%i\n", xxx);
}
}
}
// thread 2
{ // the_ptr.dec_ref();
if (0 == atomic_sub(...)) {
{ //~object()
auto xxx = a;
printf("%i\n", xxx);
}
}
}
但是,如果我們只有atomic_sub()的「釋放」語義,則此c ODE可以優化的方式:
// thread 2
auto xxx = the_ptr->a; // "auto xxx = a;" from destructor moved here
{ // the_ptr.dec_ref();
if (0 == atomic_sub(...)) {
{ //~object()
printf("%i\n", xxx);
}
}
}
但這樣一來,析構函數並不總是打印「一」的最後一個值(此代碼賽馬不免費的了)。這就是爲什麼我們還需要爲atomic_sub獲取語義(或者,嚴格來說,當計數器在遞減後變爲0時,我們需要一個獲取屏障)。
「談論編譯器在原子操作之前移動刪除」 - 1:23:34:「代碼停留在下面和上面」;;;; 「或者只是指副作用何時對其他線程可見。」 - 哪些副作用?讀取 - 修改 - 寫入每次查看修改順序中的最後一個值 – qble 2013-02-14 19:04:10
「但放鬆會帶來更多問題。」 - 哪些問題? – qble 2013-02-14 19:04:48
_「哪些問題?」放鬆的操作根本不是同步操作。 – 2013-02-14 19:06:33