ARTICLE AD BOX
I have attempted to understand the behaviors of different memory orders using only the single concept of visibility.
Visibility: (The following is my understanding of visibility, which focuses on the core concept of "being visible".)
Being visible: After a thread performs a write operation on an inter-thread shared variable, the modification result brought about by this write operation can be successfully read by a subsequent read operation of another thread. Note: The antonym of being visible is not yet visible, rather than invisible. Not yet visible means that the modification from a thread's write operation on an inter-thread shared variable has not been read by another thread's read operation for the time being (and may eventually be read subsequently).memory_order_relaxed: This memory order only guarantees that the operations on the corresponding atomic variable are atomic (with no intermediate states). However, for other threads, the modification result of this atomic operation is temporarily in a "not yet visible" state, and there are no additional constraints to avoid such a timing delay.
memory_order_release: This memory order will "package" the modification results of all write operations that occur before this release operation in the current thread (e.g., assignment operations of any other non-atomic variables, or memory_order_relaxed operations of other atomic variables, etc.). When another thread successfully reads the value written by this release operation through a corresponding acquire operation, the modification results of these preceding write operations can be deterministically visible to that thread.
memory_order_acquire: This memory order ensures that all read operations occurring after this acquire operation in the current thread can read the valid state obtained when the acquire operation is executed.
If an acquire read operation successfully reads the value written by a preceding release write operation, then the acquire operation itself (in relation to its associated read logic) and all subsequent operations in the current thread can read the last modification made to the shared variable by the thread owning the release operation, prior to that release operation. In other words, all modifications made by the thread owning the release operation before the release operation are deterministically visible to all read operations in the current thread that occur after this acquire operation.
In summary, I have attempted to explain the various behaviors of C++ memory order using the concept of "visibility" as I understand it. Based on this, my questions are as follows:
Question
Can my understanding of "visibility" correctly explain the behaviors of C++ memory order in most scenarios? If my understanding is incorrect or flawed, could you please provide an accurate explanation at the C++ language level (rather than focusing on low-level implementations)?
(Additional note: In the local technical community of my region, members always tend to explain C++ memory order through low-level system principles, such as compiler instruction reordering, CPU instruction reordering, and inconsistent CPU caching. However, to me, these low-level systems are just like a "black box" — there are numerous and complex underlying reasons that can lead to the unusual behaviors of memory order. To put it exaggeratedly, I could even casually make up an unfounded claim like "this is because my computer uses a quantum processor, and we need to observe quantum bits to determine the value of a variable". In short, I believe that explaining C++ memory order through principles below the C++ language level is both one-sided and prone to misunderstandings and errors.)
