簡體   English   中英

在這種情況下,我可以不使用釋放和獲得障礙嗎?

[英]Can I not use release and acquire barriers in this case?

本教程說明以下內容:

x86 / 64上的每個負載都已經隱含了獲取語義,每個商店都隱含了釋放語義。

現在說我有以下代碼(我在注釋中寫了問題):

/* Global Variables */

int flag = 0;
int number1;
int number2;

//------------------------------------

/* Thread A */

number1 = 12345;
number2 = 678910;
flag = 1; /* This is a "store", so can I not use a release barrier here? */

//------------------------------------

/* Thread B */

while (flag == 0) {} /* This is a "load", so can I not use an acquire barrier here? */
printf("%d", number1);
printf("%d", number2);

本教程討論的是程序集/機器級別的加載存儲 ,您可以在其中依賴x86 ISA語義,包括加載時獲取和存儲中釋放。

但是,您的代碼是C ,它根本不提供任何此類保證,並且編譯器可以自由地將其轉換為與加載和存儲所期望的完全不同的東西。 這不是理論上的,而是在實踐中發生的。 因此,簡短的答案是:不,不可能合法地在C語言中進行可移植的操作-盡管如果幸運的話,它可能會起作用。

假設代碼是用C99編寫的,目標體系結構是x86。 x86的強存儲模型僅在機器代碼級別有效。 C99沒有內存模型。 我將解釋可能出問題的地方,並討論是否存在符合C99的問題處理方式。

首先,我們必須確保所有變量都沒有被優化,並且對flagnumber1number2所有訪問都發生在內存中,而不是緩存在CPU寄存器1中 這可以在C99中通過使用volatile限定所有三個變量來實現。

其次,我們必須確保第一個線程中要flag的存儲具有釋放語義。 這些語義包括兩個保證: flag的存儲不會與先前的內存訪問一起重新排序,並使存儲對第二個線程可見。 volatile關鍵字告訴編譯器對變量的訪問可能會有明顯的副作用。 這樣可以防止編譯器針對其他操作重新排序對易失性變量的訪問,其他操作也被編譯器視為具有可觀察到的副作用。 也就是說,通過使所有三個變量均為volatile ,編譯器將在第一個線程中維護所有三個存儲的順序。 也就是說,如果在存儲之上或之下有其他非易失性存儲器訪問要flag ,則仍可以對這些訪問進行重新排序。 因此,標准volatile僅提供部分發布語義。

第三...實際上對於您的特定代碼段,不需要原子性。 這是因為要flag的存儲區僅更改一位,這本質上是原子的。 因此,對於此特定代碼,您不必擔心原子性。 但是通常,如果存儲到flag可能改變多個位,並且如果第二個線程中檢查的條件取決於看到的是全部還是部分位改變,則行為可能有所不同,那么您肯定需要確保訪問到'flag'是原子的。 不幸的是,C99沒有原子性的概念。

要獲得完整的發布語義和原子性,您可以使用C11原子(如您引用的文章中所述),也可以訴諸於編譯器特定的技術(也如您所引用的文章中所述)。 當然,您仍然可以只查看生成的機器代碼,並查看x86內存模型本身是否提供了正確性的必要要求。 在大型代碼庫上這是不可行的。 另外,下次編譯代碼時,生成的機器代碼可能會更改。 最后,由於您只是人,所以您可能會犯錯。


(1)在引用的文章中,變量A被聲明為共享全局變量。 現在,編譯器很可能將從內存中分配它。 但這是否嚴格符合標准? 是什么阻止編譯器在程序的整個生命周期內將其分配到寄存器中? 不確定。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM