亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

VarHandle 獲取/設置不透明

VarHandle 獲取/設置不透明

慕容森 2022-12-21 13:07:14
我一直在努力了解什么VarHandle::setOpaque,真正VarHandle::getOpaque在做什么。到目前為止,這并不容易——我想我得到了一些東西(但不會在問題本身中提出它們,不要混淆水域),但總的來說,這對我來說充其量是錯誤的。文檔:返回變量的值,按程序順序訪問...那么在我的理解中,如果我有:int xx = x; // read xint yy = y; // read y這些讀數可以重新排序。另一方面,如果我有:// simplified code, does not compile, but reads happen on the same "this" for exampleint xx = VarHandle_X.getOpaque(x); int yy = VarHandle_Y.getOpaque(y);這次不能重新訂貨了?這就是“程序順序”的意思?我們是在談論在這里插入障礙以禁止這種重新排序嗎?如果是這樣,因為這是兩個負載,是否可以實現相同的目標?通過: int xx = x; VarHandle.loadLoadFence() int yy = y;但它變得更加棘手:...但不能保證相對于其他線程的內存排序效果。我無法舉出一個例子來假裝我理解這部分。在我看來,這份文檔是針對那些確切地知道他們在做什么的人(我絕對不是)......那么有人可以在這里闡明一下嗎?
查看完整描述

3 回答

?
一只名叫tom的貓

TA貢獻1906條經驗 獲得超3個贊

那么在我的理解中,如果我有:

int xx = x; // read x
int yy = y; // read y

這些讀數可以重新排序。

這些讀取可能不僅碰巧被重新排序,它們可能根本不會發生。該線程可能使用舊的、以前讀取的值x和/或y它之前寫入這些變量的值,而實際上,寫入可能尚未執行,因此“讀取線程”可能使用值,而不是其他線程當時可能知道并且不在堆內存中(并且可能永遠不會)。

另一方面,如果我有:

// simplified code, does not compile, but reads happen on the same "this" for example
int xx = VarHandle_X.getOpaque(x); 
int yy = VarHandle_Y.getOpaque(y);

這次不能重新訂貨了?這就是“程序順序”的意思?

簡單地說,不透明讀寫的主要特征是,它們會實際發生。這意味著它們不能相對于至少具有相同強度的其他內存訪問重新排序,但這對普通讀寫沒有影響。

術語程序順序由 JLS 定義:

… t的程序順序是一個總順序,它反映了根據t的線程內語義執行這些操作的順序。

這是為表達式和語句指定的評估順序。我們感知效果的順序,只要只涉及一個線程。

我們是在談論在這里插入障礙以禁止這種重新排序嗎?

不,不涉及障礙,這可能是短語“ ......但不能保證相對于其他線程的內存排序效果”背后的意圖。

或許,我們可以說不透明訪問的工作方式有點像volatileJava 5 之前的版本,強制讀取訪問以查看最近的堆內存值(這只有在寫入端也使用不透明或更強大的模式時才有意義),但沒有對其他讀取或寫入的影響。

那么你能用它做什么呢?

一個典型的用例是一個取消或中斷標志,它不應該建立一個happens-before關系。通常,已停止的后臺任務沒有興趣感知停止任務在發出信號之前所做的操作,而只會結束自己的活動。因此,使用不透明模式寫入和讀取標志足以確保最終注意到信號(與正常訪問模式不同),但不會對性能產生任何額外的負面影響。

同樣,后臺任務可以寫入進度更新,如百分比數字,報告 (UI) 線程應該及時注意到,而在最終結果發布之前不需要happens-before關系。

如果您只想對 和 進行原子訪問,而沒有任何其他影響,它也很有longdouble。

由于使用字段的真正不可變對象final不受數據競爭的影響,因此您可以使用不透明模式及時發布不可變對象,而不會產生發布/獲取模式發布的更廣泛影響。

一種特殊情況是定期檢查預期值更新的狀態,一旦可用,就以更強的模式查詢該值(或顯式執行匹配的圍欄指令)。原則上,無論如何只能在寫入和后續讀取之間建立happens-before關系,但由于優化器通常沒有識別此類線程間用例的視野,因此性能關鍵代碼可以使用不透明訪問來優化這樣的場景。


查看完整回答
反對 回復 2022-12-21
?
人到中年有點甜

TA貢獻1895條經驗 獲得超7個贊

不透明意味著執行不透明操作的線程保證按照程序順序觀察自己的動作,僅此而已。

其他線程可以自由地以任何順序觀察線程的動作。在 x86 上這是一個常見的情況,因為它有

使用存儲緩沖區轉發命令寫入

內存模型,因此即使線程在加載之前確實存儲。存儲可以緩存在存儲緩沖區中,并且在任何其他內核上執行的某些線程以相反的順序加載存儲而不是存儲加載來觀察線程操作。所以不透明操作是在 x86 上免費完成的(在 x86 上我們實際上也有免費獲取,有關其他一些架構及其內存模型的詳細信息,請參閱這個極其詳盡的答案:https ://stackoverflow.com/a/55741922/8990329 )

為什么有用?好吧,我可以推測,如果某個線程觀察到一個存儲在不透明內存語義中的值,那么后續讀取將觀察到“至少這個或以后”的值(普通內存訪問不提供這樣的保證,是嗎?)。

此外,由于 Java 9 VarHandles 在某種程度上與 CI 中的獲取/釋放/消費語義相關,因此值得注意的是,不透明訪問類似于memory_order_relaxed標準中定義的內容,如下所示:

對于memory_order_relaxed,沒有操作命令內存。


查看完整回答
反對 回復 2022-12-21
?
白衣染霜花

TA貢獻1796條經驗 獲得超10個贊

我自己一直在與不透明作斗爭,文檔當然不容易理解。

從上面的鏈接:

不透明操作是按位原子和連貫有序的。

按位原子部分是顯而易見的。一致排序意味著加載/存儲到單個地址有一些總順序,每個到達看到它之前的最近地址并且順序與程序順序一致。有關一些連貫性示例,請參閱以下JCStress 測試。

Coherence 不在不同地址的加載/存儲之間提供任何排序保證,因此它不需要提供任何柵欄以便對不同地址的加載/存儲進行排序。

使用不透明,編譯器將發出它看到的加載/存儲。但是仍然允許底層硬件將加載/存儲重新排序到不同的地址。

我將您的示例升級為消息傳遞石蕊測試:

thread1:

X.setOpaque(1);

Y.setOpaque(1);


thread2:

ry = Y.getOpaque();

rx = X.getOpaque();

if (ry == 1 && rx == 0) println("Oh shit");

在允許重新排序 2 個存儲或 2 個加載(同樣是 ARM 或 PowerPC)的平臺上,上述操作可能會失敗。不透明不需要提供因果關系。JCStress 也有一個很好的例子。


此外,以下 IRIW 示例可能會失?。?/p>


thread1:

X.setOpaque(1);


thread2:

Y.setOpaque(1);


thread3:

rx_thread3 = X.getOpaque();

[LoadLoad]

ry_thread3 = Y.getOpaque();


thread4:

ry_thread4 = Y.getOpaque();

[LoadLoad]

rx_thread4 = X.getOpaque();

難道我們最終得到 rx_thread3=1,ry_thread3=0,ry_thread4=1 而 rx_thread4 為 0 嗎?


對于不透明,這可能會發生。即使負載被阻止重新排序,不透明訪問也不需要多副本原子性(可以看到不同 CPU 發出的不同地址的存儲以不同的順序)。


Release/acquire 比 opaque 強,因為 release/acquire 允許失敗,因此 opaque 允許失敗。所以 Opaque 不需要提供共識。


查看完整回答
反對 回復 2022-12-21
  • 3 回答
  • 0 關注
  • 139 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號