You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

设置共享变量时,应使用Critical Section还是memory barrier?

Answer

First off, go with the memory barrier approach—it’s the precise tool for your problem, whereas a critical section is overkill and doesn’t address the core issue correctly unless you add unnecessary extra code. Let’s break this down:

The Root Problem: Reordering & Visibility

Your issue comes down to two low-level behaviors:

  1. Compiler/CPU instruction reordering: Compilers and CPUs will rearrange store operations (like writing to number1, number2, and flag) to optimize performance, as long as the single-threaded logic of Thread A stays intact. That means flag = 1 could execute before your number assignments, breaking Thread B’s wait-and-read logic.
  2. Cache visibility: Even if instructions run in order, the values for number1 and number2 might get stuck in Thread A’s CPU cache and not be visible to Thread B immediately—until a cache flush occurs.

Why Memory Barriers Fix This

A memory barrier (like std::memory_order_release in modern C++, or platform-specific ones such as mfence on x86 or dmb on ARM) enforces two critical guarantees:

  • Ordering: All store operations before the barrier must finish executing before any stores after it run. So number1 and number2 are guaranteed to be written before flag = 1.
  • Visibility: The barrier flushes cached writes to main memory, ensuring Thread B will see the updated numbers as soon as it detects flag = 1.

Also, note that Thread B needs to read flag correctly to avoid the compiler optimizing the while loop into an infinite loop. In C, declare flag as volatile or use an atomic load with appropriate ordering (like std::memory_order_acquire for C++).

Why Critical Sections Aren’t the Right Fit

Critical sections (e.g., EnterCriticalSection/LeaveCriticalSection on Windows) are designed for mutual exclusion—stopping multiple threads from modifying shared data at the same time. In your scenario:

  • Thread B is only reading values, not writing them. There’s no risk of concurrent write conflicts here.
  • Wrapping just flag = 1 in a critical section doesn’t fix the reordering issue for number1 and number2—you’d have to wrap the entire assignment block, which uses a heavy lock for a problem a lightweight barrier solves.
  • Even if you did use a critical section in Thread A, Thread B’s reads still might not see the latest values unless you also wrap its code in the same critical section—adding unnecessary overhead.

Example of Correct Memory Barrier Usage (C++11+)

Modern C++ atomic variables handle barriers automatically with explicit ordering:

#include <atomic>

std::atomic<bool> flag(false);
int number1;
int number2;

// Thread A
number1 = 12345;
number2 = 678910;
flag.store(true, std::memory_order_release);

// Thread B
while (!flag.load(std::memory_order_acquire)) {}
printf("%d", number1);
printf("%d", number2);

The release store ensures prior writes are visible before the flag is set, and the acquire load ensures Thread B’s subsequent reads see those writes.

For plain C without C++11, use a platform-specific barrier. On x86, that looks like:

/* Thread A */
number1 = 12345;
number2 = 678910;
_mfence(); // Memory fence
flag = 1;

/* Thread B */
while (flag == 0) {}
printf("%d", number1);
printf("%d", number2);

Key Takeaway

Use memory barriers (or atomic operations with proper ordering) when you need to enforce visibility and instruction order between threads without mutual exclusion. Reserve critical sections for protecting shared data from concurrent modifications.

内容的提问来源于stack exchange,提问作者James

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:09:07