自旋锁简介

自旋锁通过 CAS(Compare-And-Swap)操作,在大多数场景下可以实现比 std::mutex 更高的性能。其基本思想是:通过原子操作让所有线程竞争同一个原子变量,以此实现互斥。

性能对比

无竞争情况下:

  1. spin_lock:16000W 次/秒
  2. mutex:2800W 次/秒
  3. std::atomic<int>:24100W 次/秒

可见自旋锁的性能相当可观。

5 个线程竞争情况下:

  1. spin_lock:717W 次/秒
  2. mutex:509W 次/秒
  3. atomic<int>:5900W 次/秒

高竞争场景下,自旋锁的性能依然高于 mutex。

实现代码

以下是基础版本的实现。调整原子操作的内存顺序可以进一步提升性能,这里暂时使用默认方式。

/*
Email me@dreamyouxi.com

自旋锁

*/
#pragma  once
#include <atomic>
#include <thread>
#include <chrono>

//cas
class spin_lock
{
private:
	std::atomic<bool> _internal_tag = false;
private:
	spin_lock(const spin_lock &) = delete;
	spin_lock& operator = (const spin_lock&) = delete;
public:
	void lock();
	void unlock();
};

class spin_lock_guard
{
	spin_lock & locker;// ref
public:
	spin_lock_guard(spin_lock& lock);
	~spin_lock_guard();
};
#include "util/log.h"
#include "concurrency/spin_lock.h"
const char LOG_NAME[] = "spin_lock";
using namespace  std;

void spin_lock::lock()
{
	bool except = false;
	int times = 0;
	//fast pass
	while (true)
	{
		if (_internal_tag.compare_exchange_strong(except, true, std::memory_order_seq_cst))
		{
			//ok got it
			return;
		}
		if (++times > 4000)
		{
			//
			break;
		}
		except = false;
	}
	times = 0;
	//slow pass
	while (true)
	{
		if (_internal_tag.compare_exchange_strong(except, true, std::memory_order_seq_cst))
		{
			//ok got it
			return;
		}
		if (++times < 1000)
		{
			//
			this_thread::yield();
		}
        else
        {
           break;
        }
		except = false;
	}
	//
	//very slow pass
	while (true)
	{
		if (_internal_tag.compare_exchange_strong(except, true, std::memory_order_seq_cst))
		{
			//ok got it
			return;
		}
		except = false;
		//内存延时 6代E5 大概是100纳秒一个周期
		//mutex 无竞争情况下 性能大概是每秒300W 次 即 0.3微秒 300纳秒
		//sleep_for 进度在win平台好像最小是1MS 因此这里的设定也许有BUG
		this_thread::sleep_for(std::chrono::nanoseconds(300));
	}
}

void  spin_lock::unlock()
{
	_internal_tag.store(false, std::memory_order_seq_cst);
}

spin_lock_guard::spin_lock_guard(spin_lock& lock) :
locker(lock)
{
	locker.lock();
}

spin_lock_guard::~spin_lock_guard()
{
	locker.unlock();
}

最高性能版本

/*
Email me@dreamyouxi.com

自旋锁

*/
#pragma  once
#include <atomic>
#include <thread>
#include <chrono>

//cas
class spin_lock
{
	std::atomic_flag locker = ATOMIC_FLAG_INIT;  // NOLINT
public:
	spin_lock(const spin_lock &) = delete;
	spin_lock& operator = (const spin_lock&) = delete;

	spin_lock() = default;

	void unlock()
	{
		locker.clear(std::memory_order_release);
	}
	void lock();
private:
	bool try_lock()
	{
		bool ok = locker.test_and_set(std::memory_order_acquire);
		return !ok;
	}
};

class spin_lock_guard
{
	spin_lock & locker;// ref
public:
	spin_lock_guard(spin_lock& lock);
	~spin_lock_guard();
};



#include "util/log.h"
#include "concurrency/spin_lock.h"
const char LOG_NAME[] = "spin_lock";
using namespace  std;

void spin_lock::lock()
{
	int times = 0;
	//fast pass
	while (true)
	{
		if (try_lock())
		{
			//ok got it
			return;
		}
		if (++times > 4000)
		{
			//
			break;
		}
	}
	times = 0;
	//slow pass
	while (true)
	{
		if (try_lock())
		{
			//ok got it
			return;
		}
		if (++times < 1000)
		{
			//
			this_thread::yield();
		}
        else
        {
           break;
        }
	}
	//
	//very slow pass
	while (true)
	{
		if (try_lock())
		{
			//ok got it
			return;
		}
		//内存延时 6代E5 大概是100纳秒一个周期
		//mutex 无竞争情况下 性能大概是每秒300W 次 即 0.3微秒 300纳秒
		//sleep_for 进度在win平台好像最小是1MS 因此这里的设定也许有BUG
		this_thread::sleep_for(std::chrono::nanoseconds(300));
	}
}

spin_lock_guard::spin_lock_guard(spin_lock& lock) :
locker(lock)
{
	locker.lock();
}

spin_lock_guard::~spin_lock_guard()
{
	locker.unlock();
}