簡體   English   中英

MT 程序如何用“非順序”語義證明是正確的?

[英]How are MT programs proven correct with “non sequential” semantics?

This could be a language neutral question, but in practice I'm interested with the C++ case: how are multithread programs written in C++ versions that support MT programming, that is modern C++ with a memory model, ever proven correct?

在舊的 C++ 中,MT 程序只是根據 pthread 語義編寫並根據 pthread 規則進行驗證,這在概念上很簡單:正確使用原語並避免數據競爭。

現在,C++ 語言語義是按照 memory model 定義的,而不是按照原始步驟的順序執行定義的。 (標准還提到了“抽象機器”,但我不再明白它的含義。)

C++ 程序如何用非順序語義證明是正確的? 誰能解釋一個程序沒有一個接一個地執行原始步驟

It is "conceptually easier" with the C++ memory model than it was with pthreads prior to the C++ memory model. C++ prior to the memory model interacting with pthreads was loosely specified, and reasonable interpretations of the specification permitted the compiler to "introduce" data races, so it is extremely difficult (if possible at all) to reason about or prove correctness for MT algorithms in舊 C++ 與 pthreads 的上下文。

這個問題似乎存在一個根本的誤解,因為 C++ 從未被定義為原始步驟的順序執行。 表達式評估之間總是存在部分排序的情況。 並且編譯器可以移動這些表達式,除非受到限制。 引入 memory model 后,這一點沒有改變。 memory model 為單獨的執行線程之間的評估引入了部分順序。

The advice "use the primitives correctly and avoid data races" still applies, but the C++ memory model more strictly and precisely constrains the interaction between the primitives and the rest of the language, allowing more precise reasoning.

在實踐中,要證明這兩種情況下的正確性並不容易。 大多數程序都沒有被證明是無數據競爭的。 嘗試盡可能多地封裝任何同步,以便對較小的組件進行推理,其中一些組件可以被證明是正確的。 還有一個使用地址清理器和線程清理器等工具來捕獲數據競爭。

在數據競賽中, POSIX 說

應用程序應確保多個控制線程(線程或進程)對任何 memory 位置的訪問受到限制,以便控制線程無法讀取或修改 memory 位置,而另一個控制線程可能正在修改它。 使用同步線程執行和同步 memory 相對於其他線程的函數來限制此類訪問......應用程序可能允許多個控制線程同時讀取 memory 位置。

在數據競賽中, C++ 說

如果程序的執行包含兩個潛在的並發沖突操作,則程序的執行包含數據競爭,其中至少一個不是原子的,並且兩者都不會在另一個之前發生,除了下面描述的信號處理程序的特殊情況。 任何此類數據競爭都會導致未定義的行為。

C++ 定義了更多術語並試圖更精確。 其要點是,兩者都禁止數據競爭,兩者都被定義為沖突訪問,而不使用同步原語。

POSIX 說 pthread 函數相對於其他線程同步 memory 這是未指定的。 One could reasonably interpret that as (1) the compiler cannot move memory accesses across such a function call, and (2) after calling such a function in one thread, the prior actions to memory from that thread will be visible to another thread after it調用這樣的 function。 這是一種常見的解釋,可以通過將函數視為不透明並可能破壞所有 memory 來輕松實現。

作為這個松散規范問題的一個例子,編譯器仍然被允許引入或刪除 memory 訪問(例如,通過寄存器提升和溢出)並進行比必要更大的訪問(例如,接觸結構中的相鄰字段)。 因此,編譯器可以完全正確地“引入”未直接寫入源代碼的數據競爭。 C++11 memory model 阻止它這樣做。

C++ 說,關於互斥鎖

同步:同一 object 上的先前 unlock() 操作應與此操作同步。

所以 C++ 更具體一點。 您必須鎖定和解鎖同一個互斥鎖才能進行同步。 但鑒於此, C++ 表示解鎖前的操作對新儲物櫃可見:

如果...存在評估 B 和 C 使得 A 在 B 之前排序,B 僅在 C 之前發生,並且 Z0D61F8370CAD1D412F80B84D143E125 之前發生在 D61F8370CAD1D412F80B84D143E127 之前,則評估 A強烈發生在評估 D 之前。 B,那么在所有情況下,A 似乎都在 B 之前被評估。 強烈發生在排除消費操作之前。 ——尾注]

(B = 解鎖,C = 鎖定,B 簡單地發生在 C 之前,因為 B 與 C 同步。下一個 so 序列之前是一個完整的表達式序列的概念,用於執行的單線程。)

因此,如果您將自己限制在 pthread 中存在的各種原語(鎖、條件變量等),以及 pthread 提供的保證類型(順序一致性),C++ 應該不會增加任何驚喜。 事實上,它消除了一些意外,增加了精度,並且更適合正確性證明。

The article Foundations of the C++ Concurrency Memory Model is a great, expository read for anyone interested in this topic about the problems with the status quo at the time and the choices made to fix them in the C++11 memory model.


編輯更清楚 state 問題的前提是有缺陷的,使用 memory model 更容易推理,並添加對 Boehm 的一些闡述的參考。

暫無
暫無

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

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