簡體   English   中英

C ++是否保證從兩個線程訪問數組的相鄰元素是安全的

[英]Does C++ guarantees it is safe to access adjacent elements of an array from two threads

就C ++標准而言(C ++ 11及更高版本,我猜,因為在不考慮線程之前),並發寫入數組的不同的 ,可能相鄰的元素是否安全?

例如:

#include <iostream>
#include <thread>

int array[10];

void func(int i) {
   array[i] = 42;
}

int main() 
{
   for(int i = 0; i < 10; ++i) {
      // spawn func(i) on a separate thread
      // (e.g. with std::async, let me skip the details)
   }
   // join

   for(int i = 0; i < 10; ++i) {
      std::cout << array[i] << std::endl; // prints 42?
   }

   return 0;
}

在這種情況下,語言是否保證數組的不同元素的寫入不會導致競爭條件? 它是否適用於任何類型,或者是否有任何安全要求?

數據競爭僅發生在相同的內存位置,即只有當&x == &y才能在兩個glvalues xy上進行數據競爭。

[intro.races] / 2

如果其中一個修改內存位置而另一個讀取或修改相同的內存位置,則兩個表達式評估會發生沖突。

[intro.races] / 21

如果一個程序的執行包含兩個可能同時發生沖突的動作,那么該程序的執行包含數據競爭[...]

其余部分不適用於此處。 所以你的陣列沒有數據競爭。

是。

來自https://en.cppreference.com/w/cpp/language/memory_model

當表達式的評估寫入內存位置而另一個評估讀取或修改相同的內存位置時,表達式會發生沖突。 具有兩個相互沖突的評估的程序具有數據競爭,除非[...]

然后:

內存位置

  • 標量類型的對象(算術類型,指針類型,枚舉類型或std :: nullptr_t)
  • 或非零長度的最大連續的位域序列

因此,如果數組的元素存儲在不同的內存位置,則不會產生沖突的評估。

一個數組是:

表格T a[N]; ,將a聲明為一個數組對象,由N個連續分配的T類型對象組成。

由於兩個不同的對象不能具有相同的地址,因此它們及其組成者不能具有相同的存儲位置。 這保證了對早期要求的滿意度。

此外,對象可以包含多個內存位置,因此您甚至可以讓兩個線程對同一對象的不同成員進行操作!

請注意,為了使您的示例正確,連接也必須正確編寫,但它與數組的相鄰元素無關,而是在同一個元素上運行,所以我想它超出了問題的范圍。


個人說明:順便說一句。 如果不能保證這一點,那么如果不在標准庫中進行無用的並行計算,則會嚴重限制。

是的,但“確定”並不意味着這樣做很聰明。

有幾個問題需要考慮,也許最重要的一個是CPU緩存。 在例如x86上,高速緩存行是64字節長,因此每個線程應該例如在與高速緩存行長度匹配的陣列塊上工作以避免例如錯誤共享。

這是一個例子: 假共享SO問題/答案

並行訪問不同線程上的連續元素是安全的 ,但如果頻繁發生, 則可能導致代碼中出現性能問題。 這是由於現代CPU上並行性的基本限制。

對於大多數程序來說,內存訪問是一個主要的瓶頸。 必須仔細編寫代碼的性能關鍵部分,以避免過多的緩存未命中。 緩存有多個級別,每個級別比前一個級別更快。 但是,當數據不在緩存中時,或者數據可能已被另一個CPU更改時,必須將其重新加載到緩存中。

CPU無法跟蹤每個字節的狀態,因此它會跟蹤稱為高速緩存行的字節塊。 如果高速緩存行中的任何字節由單獨的CPU更改,則必須重新加載以確保同步。

在不同線程上訪問單獨的字節只會導致重新加載,如果它們位於同一緩存行中。 並且因為高速緩存行是連續的,所以從不同的線程訪問連續的元素通常會導致必須將存儲器重新加載到高速緩存中。 這稱為虛假共享,如果性能受到關注,應在並行代碼中避免使用。

話雖這么說,如果很少發生它可能很好,你應該在優化它之前對你的代碼進行基准測試和測試。

暫無
暫無

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

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