2014-10-20 124 views
2

我使用boost映射文件庫打開一個文件。 是否可以在該映射文件上使用「fetch_add」(在特定位置讀取值,然後與另一個位置一起添加並以原子方式將其寫回到同一位置)?C++:內存映射文件上的Fetch_add

如果多個線程寫入它並行可能有問題,而原子參與

該文件爲二進制格式,幷包含整數或雙打(取決於具體的文件)。

我也嘗試過鎖/互斥鎖,但是它們在使用多線程時總是放慢我的程序。與算法的其餘部分相比,在鎖定區域花費的時間很長,並且線程彼此阻塞。

有沒有更好的方法讓多個線程可以寫入高性能的映射文件?

謝謝。 Laz

回答

1

有沒有多個進程映射此文件,或只是多個線程?

如果多個進程正在同時訪問此內存映射文件,則必須執行您自己的(進程間)同步。

如果它只有多個線程,那麼你可以按照與其他任何內存單詞相同的方式自動更新內存,但不能使用std::atomic(因爲顯然這些字節直接對應於文件中的一部分,而不是std::atomic結構)。 因此,您必須訴諸於使用您的特定平臺的支持,以便通過Win32(或帶有g ++的__sync_fetch_and_add)上的InterlockedIncrement來在x86上原子級修改內存,即lock xadd。請注意確保內存順序語義(和返回值!)符合您的預期。但是,如果你需要的話,用特定於平臺的方式包裝特定於平臺的功能可能有點麻煩,所以在這種情況下,我建議將同時訪問的數據保存在單獨的std::atomic中變量,然後在最後更新相應的文件字節。

請注意,所有這些都與內存映射正交 - 操作系統使用內存映射文件和內存映射文件按需交換進入和退出,並且管理這些頁面的內存管理單元與內存管理單元相同它處理任意其他(非映射)頁面,因此頁面本身可以被多個線程修改,而不必擔心除通常(應用程序級別)數據競爭之外的其他任何事情。

+0

只有多個線程訪問映射。所以std :: atomic只是用於單個值,並且沒有辦法使內存塊成爲原子,就像數組一樣?除了使每個元素本身都是原子單一值?沒有考慮平臺的細節。感謝你及時的答覆! – Lazarus535 2014-10-20 21:15:16

+0

__sync_fetch_and_add的虛擬+1!太低的代表依然是:-( – Lazarus535 2014-10-20 21:25:08

+0

@ Lazarus535:'std :: atomic'是一個模板,所以理論上你可以有一個任何類型的原子變量,包括一個數組或者一個大的結構,然而,[只有某些特殊化纔是鎖定的(http://en.cppreference.com/w/cpp/atomic/atomic/is_lock_free)(理想情況下,硬件支持的所有硬件都支持,給定標準庫的有效實現),原子性通過正常所有其他類型的互斥量(實現的內部)還要注意,原子變量數組與包含數組的原子變量完全不同:-) – Cameron 2014-10-20 21:30:42

1

由於存儲器映射文件的行爲與普通存儲器[1]相似,所以它可以和任何其他存儲器一樣工作。

這將編譯(在使用clang++3.6 -Wall -Wextra),但是在技術上是未定義的(因爲std::atomic<T>是不能保證是相同類型的或具有相同的對準規則爲T):

std::atomic<uint64_t> *p; 
    p = reinterpret_cast<std::atomic<uint64_t>*>(&dest[index]); 
    p->fetch_add(value); 

這應該是克良好++和鐺++:

dest[index] = __sync_fetch_and_add(&dest[index], value); 

(兩個產生幾乎相同的彙編代碼, - 後者使用xaddq [返回原始值],其中前者使用addq - usign __sync_add_and_fetch會做同樣的addq,我希望)

[1],因爲它是普通內存 - Linux中的映射機制正是處理普通內存相同的機制,比如在交換輸入/輸出數據時,你有沒有足夠的可用內存,或者如果您在剛啓動的應用程序中使用代碼/數據。雖然我無法訪問Windows源代碼,但我相信這也是真的。其他操作系統可能會以一種完全不同的方式實現它,但是沒有理由相信它會阻止原子操作的發揮。

+0

我的確會嘗試將指針轉換爲std :: atomic以查看它是否有效。對我來說似乎很討厭:-)謝謝。 – Lazarus535 2014-10-20 21:42:56

+2

只要記住,不能保證在將來的某個時刻這不會中斷 - 或者如果您編譯了不同的處理器等。 – 2014-10-20 21:45:56