C++ 线程锁:互斥锁和自旋锁详解
在 C++ 中,可以使用线程锁来确保多个线程访问共享资源时的互斥性。C++ 提供了两种线程锁的实现:互斥锁 (mutex) 和自旋锁 (spinlock)。
- 互斥锁 (mutex): 互斥锁是 C++ 中最常用的线程锁。它可以通过 lock 和 unlock 操作来实现对共享资源的独占访问。当一个线程对互斥锁进行 lock 操作时,如果锁已经被其他线程占用,则该线程会被阻塞,直到锁被释放。当一个线程对互斥锁进行 unlock 操作时,其他线程就可以尝试获得该锁。
下面是使用互斥锁的示例代码:
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx; // 定义一个互斥锁
void printHello() {
std::lock_guard<std::mutex> lock(mtx); // 创建一个 lock_guard 对象,自动加锁,并在作用域结束时自动解锁
std::cout << 'Hello' << std::endl;
}
int main() {
std::thread t1(printHello);
std::thread t2(printHello);
t1.join();
t2.join();
return 0;
}
在上面的示例中,我们使用 std::mutex 定义了一个互斥锁 mtx。在函数 printHello 中,我们使用 std::lock_guard 对 mtx 进行加锁,并在作用域结束时自动解锁。这样,当多个线程同时调用 printHello 函数时,它们会按顺序执行,确保输出的顺序是正确的。
- 自旋锁 (spinlock): 自旋锁是一种比较简单的线程锁实现,它通过不断尝试获取锁来实现对共享资源的互斥访问。当一个线程尝试获取自旋锁时,如果锁已经被其他线程占用,则该线程会一直循环等待,直到锁被释放。
C++ 标准库并没有提供自旋锁的实现,但可以使用一些第三方库或操作系统提供的 API 来实现自旋锁。例如,可以使用 std::atomic_flag 和 std::atomic_flag_test_and_set 函数来实现一个简单的自旋锁。
下面是使用 std::atomic_flag 实现的自旋锁的示例代码:
#include <iostream>
#include <thread>
#include <atomic>
std::atomic_flag flag = ATOMIC_FLAG_INIT; // 定义一个原子标志,初始为未设置状态
void printHello() {
while (flag.test_and_set(std::memory_order_acquire)) {
// 自旋等待锁的释放
}
std::cout << 'Hello' << std::endl;
flag.clear(std::memory_order_release); // 释放锁
}
int main() {
std::thread t1(printHello);
std::thread t2(printHello);
t1.join();
t2.join();
return 0;
}
在上面的示例中,我们使用 std::atomic_flag 定义了一个原子标志 flag,并初始化为未设置状态。在函数 printHello 中,我们使用 flag.test_and_set(std::memory_order_acquire) 来尝试获取自旋锁,如果锁已经被其他线程占用,则会一直循环等待。当获取到锁后,我们输出 'Hello',然后使用 flag.clear(std::memory_order_release) 来释放锁。
需要注意的是,自旋锁在多核系统上的效果可能比互斥锁差,因为它会占用 CPU 资源,造成浪费。因此,在实际使用中,需要根据具体情况选择合适的线程锁。
原文地址: https://www.cveoy.top/t/topic/o1dR 著作权归作者所有。请勿转载和采集!