目录
互斥锁
在C++中,实现线程同步的一种常见方法是使用互斥锁(mutex)。互斥锁能够确保同一时刻只有一个线程可以访问某个共享资源。C++11及以后版本引入了<mutex>
库,其中包含了std::mutex
类以及与之相关的锁定机制(如std::lock_guard
和std::unique_lock
)。
以下是一个使用std::mutex
和std::thread
的简单线程同步示例:
#include <iostream>
#include <thread>
#include <mutex>
// 全局互斥锁
std::mutex mtx;
// 共享资源
int counter = 0;
// 线程函数
void increment(int n) {
for (int i = 0; i < n; ++i) {
// 锁定互斥锁
std::lock_guard<std::mutex> lck(mtx);
// 安全地访问共享资源
++counter;
// 当std::lock_guard对象lck超出作用域时,自动释放互斥锁
}
}
int main() {
std::thread t1(increment, 1000);
std::thread t2(increment, 1000);
// 等待线程完成
t1.join();
t2.join();
std::cout << "Final counter: " << counter << std::endl;
return 0;
}
在这个示例中,我们定义了一个全局的std::mutex
对象mtx
来保护共享资源counter
。然后,我们定义了一个线程函数increment
,它接受一个整数n
作为参数,并将counter
增加n
次。在每次增加之前,我们使用std::lock_guard<std::mutex>
来锁定互斥锁mtx
。std::lock_guard
是一个RAII(Resource Acquisition Is Initialization)风格的锁管理类,它在构造时自动锁定互斥锁,并在其析构时(即离开作用域时)自动释放互斥锁。这样,我们就无需手动调用lock()
和unlock()
,从而减少了忘记释放锁的风险。
最后,我们在main
函数中创建了两个线程t1
和t2
,它们分别调用increment
函数来增加counter
的值。通过join()
函数,我们等待这两个线程完成后再继续执行,最后打印出counter
的最终值。由于我们使用了互斥锁来保护counter
,因此最终打印的值应该是2000,而不是由于线程竞争而导致的不确定值。
临界区
在C++中,critical_section
通常指的是一个用于多线程同步的机制,用于保护共享资源或代码段,以防止多个线程同时访问导致的竞态条件或数据不一致问题。然而,需要注意的是,critical_section
这个术语并不是C++标准库(STL)中的直接组成部分,但在Windows编程中,它通常与CRITICAL_SECTION
结构体相关联,该结构体用于管理临界区。
Windows平台下的CRITICAL_SECTION
在Windows平台上,CRITICAL_SECTION
是一个用于线程同步的轻量级对象,它只适用于同一进程内的线程同步。使用CRITICAL_SECTION
需要包含<windows.h>
头文件,并按照以下步骤操作:
-
定义CRITICAL_SECTION对象:在代码中定义一个
CRITICAL_SECTION
类型的变量。 -
初始化CRITICAL_SECTION:使用
InitializeCriticalSection
函数初始化CRITICAL_SECTION
对象。 -
进入临界区:在需要保护的代码段之前,使用
EnterCriticalSection
函数进入临界区。 -
离开临界区:在需要保护的代码段之后,使用
LeaveCriticalSection
函数离开临界区。 -
删除CRITICAL_SECTION:当不再需要
CRITICAL_SECTION
对象时,使用DeleteCriticalSection
函数删除它。
示例代码
以下是一个Windows平台下使用CRITICAL_SECTION
的示例代码:
#include <windows.h>
#include <iostream>
CRITICAL_SECTION cs;
void CriticalSectionFunction() {
EnterCriticalSection(&cs);
// 在这里执行需要保护的代码
std::cout << "Thread " << GetCurrentThreadId() << " is in the critical section." << std::endl;
LeaveCriticalSection(&cs);
}
int main() {
InitializeCriticalSection(&cs);
// 创建并启动线程(示例中省略了线程创建和启动的代码)
// ...
// 等待所有线程完成(示例中省略了等待线程完成的代码)
// ...
DeleteCriticalSection(&cs);
return 0;
}
注意事项
CRITICAL_SECTION
是Windows特有的,不适用于跨平台的C++代码。- 在Linux等Unix-like系统中,通常使用互斥锁(如
pthread_mutex_t
)来实现类似的功能。 - 使用
CRITICAL_SECTION
时,要确保每个EnterCriticalSection
调用都有对应的LeaveCriticalSection
调用,以避免死锁。 - 在C++11及以后的版本中,推荐使用标准库中的
std::mutex
、std::lock_guard
等同步原语,因为它们更加灵活且跨平台。
C++11及以后的替代方案
在C++11及以后的版本中,可以使用<mutex>
头文件中的std::mutex
、std::lock_guard
等同步原语来替代CRITICAL_SECTION
。这些同步原语是跨平台的,并且提供了更加灵活和强大的同步功能。
例如,使用std::mutex
和std::lock_guard
的示例代码如下:
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void protectedFunction() {
std::lock_guard<std::mutex> lock(mtx);
// 在这里执行需要保护的代码
std::cout << "Thread " << std::this_thread::get_id() << " is executing protected code." << std::endl;
}
int main() {
// 创建并启动线程(示例中省略了线程创建和启动的代码)
// ...
// 等待所有线程完成(示例中省略了等待线程完成的代码)
// ...
return 0;
}