【手写实现自旋锁SpinLock】

手写实现自旋锁

概念

自旋锁(Spinlock)是一种基本的同步机制,用于保护共享资源的并发访问。它的工作原理是,当一个线程尝试获取自旋锁时,如果锁已经被其他线程持有,则该线程会一直自旋(忙等待)直到锁被释放。

核心原理

  • 自旋锁的核心思想是避免线程进入阻塞状态,以减少线程切换的开销。在获取自旋锁的过程中,线程会不断地检查锁的状态,而不是被置于等待队列中。这样可以减少线程上下文切换、调度开销和等待唤醒的时间。

  • 当一个线程成功获取自旋锁后,它可以在临界区中执行操作,并在完成后释放锁,以允许其他线程获取锁并继续执行。如果在获取锁的过程中发现锁已被其他线程持有,线程将继续自旋等待,直到锁被释放。

  • 需要注意的是,自旋锁适用于临界区很小且短暂的情况,因为自旋锁会导致线程忙等待,占用CPU资源。当临界区较大或锁的持有时间较长时,使用自旋锁可能会浪费大量的CPU时间。此外,在单核CPU上使用自旋锁也可能导致饥饿问题,因为线程会一直占用CPU资源而无法释放。

  • 自旋锁通常使用原子操作和标志位来实现,确保对共享资源的互斥访问。在现代多核处理器上,自旋锁在某些情况下可以提供较好的性能,但在其他情况下可能不如更复杂的同步机制(如互斥锁)效果好。因此,选择适当的同步机制需要综合考虑具体的应用场景和性能需求。

优缺点

下面是自旋锁的一些优点和缺点:

  • 优点:

    • 响应时间低:自旋锁避免了线程切换的开销,因为线程在获取锁时会一直自旋等待,而不会被置于等待状态。这使得在临界区很小且短暂的情况下,自旋锁的响应时间通常较低。

    • 避免上下文切换开销:相比于互斥锁(Mutex)等需要将线程从用户态切换到内核态的同步机制,自旋锁在用户态执行自旋等待,避免了上下文切换的开销,从而提高了性能。

    • 适用于多核 CPU:自旋锁在多核 CPU 上的效果较好,因为当一个线程在自旋等待时,其他线程可以继续执行,并且在多核 CPU上,线程可以在不同的核心上运行,避免了竞争条件。

  • 缺点:

    • 高 CPU 开销:自旋锁会导致线程在获取锁时进行忙等待,不断检查锁是否可用,这会占用 CPU
      资源。如果临界区较大或锁被持有的时间较长,自旋锁可能会浪费大量的 CPU 时间。

    • 不适用于单核 CPU 或独占型任务:在单核 CPU 上,自旋锁会一直占用 CPU
      资源而无法释放,导致其他任务无法执行。此外,如果一个线程长时间持有自旋锁而不释放,其他线程将无法进入临界区,这可能导致饥饿问题。

    • 死锁风险:与其他同步机制一样,自旋锁也存在死锁的风险。如果在一个线程已经获取锁的情况下,又尝试获取同一个自旋锁,就会发生死锁现象。

综上所述,自旋锁适用于临界区很小且短暂的情况,在多核 CPU 上效果较好。但对于临界区较大或锁被持有的时间较长的情况,或者在单核 CPU上使用时需要谨慎。在实际应用中,需要根据具体情况权衡自旋锁的优缺点,选择合适的同步机制。

代码实现

#include 
#include 
#include 
#include 
using namespace std;
class SpinLock {
public:
    SpinLock() : flag(false) {}

    void lock() {
        while (flag.exchange(true, std::memory_order_acquire)) {
            // 自旋等待锁释放
        }
    }

    void unlock() {
        flag.store(false, std::memory_order_release);
    }

private:
    std::atomic<bool> flag;
};

int counter = 0;
SpinLock spinLock;

void incrementCounter() {
    for (int i = 0; i < 100000; ++i) {
        spinLock.lock();
        counter++;
        spinLock.unlock();
    }
}

int main() {
    const int numThreads = 10;
    std::vector<std::thread> threads;

    for (int i = 0; i < numThreads; ++i) {
        threads.emplace_back(incrementCounter);
    }

    for (auto& thread : threads) {
        thread.join();
    }

    std::cout << "Counter: " << counter << std::endl;

    return 0;
}

github链接:https://github.com/mulinhu/CPPer/tree/main/util/spinlock_demo

你可能感兴趣的:(C++,高并发)