知用网
第二套高阶模板 · 更大气的阅读体验

线程同步机制中的临界区:避免数据冲突的关键技巧

发布时间:2026-01-18 12:30:44 阅读:236 次

什么是临界区

在多线程编程中,多个线程可能同时访问同一块共享资源,比如一个全局变量或者一段内存区域。当这些线程对资源进行读写操作时,如果没有适当的控制,就可能出现数据错乱。这种需要被保护、不能被多个线程同时访问的代码段或资源区域,就叫做“临界区”。

举个生活中的例子:想象办公室里只有一台打印机,三个人同时要打印合同。如果没人排队协调,文件可能会混在一起,页码错乱。临界区的作用,就是给这台打印机加个“使用中”的标识,确保一次只有一个人能用。

为什么需要线程同步

假设两个线程同时对一个计数器执行自增操作(i++),表面上看只是加1,但背后其实包含读取、修改、写回三个步骤。如果线程A读取了值,还没来得及写回,线程B也读取了同样的旧值,最后结果就会少算一次。

这种问题在高并发场景下特别常见,比如电商抢购库存扣减、银行账户转账等。如果不处理好临界区,轻则数据不准,重则系统崩溃。

常见的临界区实现方式

Windows API 提供了一种轻量级的同步机制——临界区(Critical Section)。它适用于同一进程内的线程同步,比互斥量更高效。

下面是一个使用临界区保护共享资源的 C++ 示例:

#include <windows.h>
#include <thread>

int shared_counter = 0;
CRITICAL_SECTION cs;

void worker() {
for (int i = 0; i < 10000; ++i) {
EnterCriticalSection(&cs);
++shared_counter;
LeaveCriticalSection(&cs);
}
}

int main() {
InitializeCriticalSection(&cs);

std::thread t1(worker);
std::thread t2(worker);

t1.join();
t2.join();

DeleteCriticalSection(&cs);
return 0;
}

在这个例子中,EnterCriticalSection 和 LeaveCriticalSection 之间的代码就是临界区。每次只有一个线程能进入,其他线程必须等待,从而保证 shared_counter 的操作是安全的。

注意事项

临界区虽然高效,但只能用于同一进程内的线程同步。跨进程或需要超时控制的场景,应该考虑使用互斥量(Mutex)或信号量(Semaphore)。

另外,使用时要避免死锁。比如线程A持有临界区后,又去等待另一个被线程B占用的资源,而线程B也在等A释放,就会卡住。就像两个人互相等着对方先开门,结果谁也不动。

实际开发中,建议尽早初始化临界区,用完及时销毁,并且确保每次进入后都有对应的退出,否则程序可能卡死或崩溃。