自旋锁 通过cas操作,在大部分情况下可以实现比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();
}