記憶體模型與原子操作練習題 (Practice - Memory Model and Atomics)


Question 1 - 位元欄位與記憶體位置 [recall]

某 struct 內有相鄰的位元欄位 bf1:10bf2:25,兩個執行緒想分別用兩把不同的 mutex 保護並同時寫入這兩個欄位,可行嗎?若不可行,如何讓它們可被獨立保護?


Question 2 - 資料競爭與原子操作的界線 [recall]

兩個執行緒無任何同步地讀寫同一個非原子 int,後果是什麼?把它改成 std::atomic<int> 後,「解決」了什麼、又「沒解決」什麼?


Question 3 - std::atomic_flag [recall]

哪個原子型別是標準唯一保證 lock-free 的?它如何初始化、支援哪些操作、有什麼致命限制?


Question 4 - compare_exchange weak vs strong [recall]

compare_exchange_weak() 回傳 false,但檢查發現變數值其實等於 expected——為什麼?weak 與 strong 各該在什麼情況使用?


Question 5 - 六種順序與三種模型 [recall]

列出六種 memory_order 並歸類到三種記憶體模型;預設是哪一種?哪一種被 C++17 明確不建議使用?


Question 6 - 釋放序列的定義 [recall]

什麼是釋放序列 (release sequence)?鏈上的讀-改-寫操作對記憶體順序有何要求?acquire 載入讀到鏈中間的值時,與誰同步?


Question 7 - 自旋鎖的記憶體順序 [application]

std::atomic_flag 實作自旋鎖:lock()unlock() 各應選什麼記憶體順序?寫出核心程式碼並說明為何臨界區內對非原子資料的修改對下一個持鎖者可見。


Question 8 - 用柵欄修復 relaxed 程式 [application]

執行緒 A 依序 x.store(true,relaxed)y.store(true,relaxed);執行緒 B 以 relaxed 迴圈等 y 為 true 後讀 x,斷言 x==true 可能失敗。不改變四個操作的 relaxed 標記,如何用 std::atomic_thread_fence 修復?柵欄位置有何硬性要求?


Question 9 - seq_cst 與 acquire-release 的差異 [analysis]

執行緒 1 寫 x、執行緒 2 寫 y(不同變數),執行緒 3 等 x 後讀 y、執行緒 4 等 y 後讀 x,讀到 true 就對 z 加一。比較:全部用 seq_cst 時 z 為何不可能為 0?全部改成 release/acquire 後為何 z==0 變合法?兩者成本差在哪?


Question 10 - 釋放序列的雙消費者分析 [analysis]

生產者填好 20 筆 queue_data 後執行 count.store(20, release);兩個消費者各自 count.fetch_sub(1, acquire) 取索引後讀取非原子的 queue_data。第二個消費者讀到的計數值(19)是第一個消費者寫入的,而非生產者——為什麼它讀 queue_data 仍然安全?若標準沒有釋放序列規則,該如何補救、代價是什麼?