Windows和POSIX中都提供了自旋锁,我们也可以通过C++11的atomic来实现自旋锁。那么两者性能上面是什么关系?先引入实现代码:

#ifndef __spinlock_h__
#define __spinlock_h__ #include <atomic> #ifdef _WIN32 #include <Windows.h> class spinlock_mutex
{
public:
static constexpr DWORD SPINLOCK_COUNT = -;
public:
// 在初始化时,会出现资源不足的问题,这里忽略这个问题
// 具体参考Critical Sections and Error Handling(Windows via C/C++)
spinlock_mutex()
{
InitializeCriticalSectionAndSpinCount(&m_cs, SPINLOCK_COUNT);
} ~spinlock_mutex()
{
DeleteCriticalSection(&m_cs);
} void lock()
{
EnterCriticalSection(&m_cs);
} bool try_lock()
{
return TryEnterCriticalSection(&m_cs) == TRUE;
} void unlock()
{
LeaveCriticalSection(&m_cs);
} private:
CRITICAL_SECTION m_cs;
}; #elif defined(_POSIX_C_SOURCE) #include <pthread.h> class spinlock_mutex
{
public:
// 这里不处理可能出现的调用错误
spinlock_mutex()
{
pthread_spin_init(&m_cs, PTHREAD_PROCESS_PRIVATE);
} ~spinlock_mutex()
{
pthread_spin_destroy(&m_cs);
} void lock()
{
pthread_spin_lock(&m_cs);
} bool try_lock()
{
return pthread_spin_trylock(&m_cs) == ;
} void unlock()
{
pthread_spin_unlock(&m_cs);
} private:
pthread_spinlock_t m_cs;
}; #else class spinlock_mutex
{
std::atomic_flag flag;
public:
spinlock_mutex() :
flag{ ATOMIC_FLAG_INIT }
{} void lock()
{
while (flag.test_and_set(std::memory_order_acquire));
} void unlock()
{
flag.clear(std::memory_order_release);
} bool try_lock()
{
return !flag.test_and_set(std::memory_order_acquire);
}
}; #endif #endif // __spinlock_h__

下面给出一个简单测试,两组线程,一组用来插入,另外一组用来取出。测试结果显示:

(1)无论是Windows,还是POSIX提供的C语言版本的自旋锁,都和C++11使用atomic构建的自旋锁效率相近。

(2)在插入线程数和取出线程数相同的情况下,线程数越多,效率越低。

下面是测试代码:

#include <memory>
#include <cassert> #include <iostream>
#include <vector>
#include <thread>
#include <future>
#include <random>
#include <chrono> #include "spinlock.h"
#include <forward_list> struct student_name
{
student_name(int age = )
: age(age), next(nullptr)
{ } int age; student_name* next;
}; spinlock_mutex g_mtx;
std::forward_list<int> g_students; std::atomic<int> g_inserts; // insert num (successful)
std::atomic<int> g_drops; // drop num (successful) std::atomic<int> g_printNum; // as same as g_drops std::atomic<long long> g_ageInSum; // age sum when producing student_name
std::atomic<long long> g_ageOutSum; // age sum when consuming student_name std::atomic<bool> goOn(true); constexpr int INSERT_THREAD_NUM = ;
constexpr int DROP_THREAD_NUM = ; constexpr int ONE_THREAD_PRODUCE_NUM = ; // when testing, no more than this number, you know 20,000,00 * 100 * 10 ~= MAX_INT if thread num <= 10 inline void printOne(student_name* t)
{
g_printNum.fetch_add(, std::memory_order_relaxed);
g_ageOutSum.fetch_add(t->age, std::memory_order_relaxed);
g_drops.fetch_add(, std::memory_order_relaxed);
delete t;
} void insert_students(int idNo)
{
std::default_random_engine dre(time(nullptr));
std::uniform_int_distribution<int> ageDi(, ); for (int i = ; i < ONE_THREAD_PRODUCE_NUM; ++i)
{
int newAge = ageDi(dre);
g_ageInSum.fetch_add(newAge, std::memory_order_relaxed); {
std::lock_guard<spinlock_mutex> lock(g_mtx);
g_students.push_front(newAge); } // use memory_order_relaxed avoiding affect folly memory order
g_inserts.fetch_add(, std::memory_order_relaxed);
}
} void drop_students(int idNo)
{
while (auto go = goOn.load(std::memory_order_consume))
{
{
std::forward_list<int> tmp;
{
std::lock_guard<spinlock_mutex> lock(g_mtx);
std::swap(g_students, tmp);
}
auto it = tmp.begin();
while (it != tmp.end())
{
g_printNum.fetch_add(, std::memory_order_relaxed);
g_ageOutSum.fetch_add(*it, std::memory_order_relaxed);
g_drops.fetch_add(, std::memory_order_relaxed);
++it;
}
}
}
} int main()
{
auto start = std::chrono::system_clock::now(); std::vector<std::future<void>> insert_threads;
std::vector<std::future<void>> drop_threads; for (auto i = ; i != INSERT_THREAD_NUM; ++i)
{
insert_threads.push_back(std::async(std::launch::async, insert_students, i));
} for (auto i = ; i != DROP_THREAD_NUM; ++i)
{
drop_threads.push_back(std::async(std::launch::async, drop_students, i)); } for (auto& thread : insert_threads)
{
thread.get();
} std::this_thread::sleep_for(std::chrono::milliseconds()); goOn.store(false, std::memory_order_release); for (auto& thread : drop_threads)
{
thread.get();
} {
std::forward_list<int> tmp;
{
std::lock_guard<spinlock_mutex> lock(g_mtx);
std::swap(g_students, tmp);
}
auto it = tmp.begin();
while (it != tmp.end())
{
g_printNum.fetch_add(, std::memory_order_relaxed);
g_ageOutSum.fetch_add(*it, std::memory_order_relaxed);
g_drops.fetch_add(, std::memory_order_relaxed);
++it;
}
} auto end = std::chrono::system_clock::now();
std::chrono::duration<double> diff = end - start;
std::cout << "Time to insert and drop is: " << diff.count() << " s\n"; std::cout << "insert count1: " << g_inserts.load() << std::endl;
std::cout << "drop count1: " << g_drops.load() << std::endl;
std::cout << "print num1: " << g_printNum.load() << std::endl; std::cout << "age in1: " << g_ageInSum.load() << std::endl;
std::cout << "age out1: " << g_ageOutSum.load() << std::endl; std::cout << std::endl;
}

关于自选锁,还有以下内容需要说明:

(1)应用层用spinlock的最大问题是不能跟kernel一样的关中断(cli/sti),假设并发稍微多点,线程1在lock之后unlock之前发生了时钟中断,
 * 一段时间后才会被切回来调用unlock,那么这段时间中另一个调用lock的线程不就得空跑while了?这才是最浪费cpu时间的地方。
 * 所以不能关中断就只能sleep了,怎么着都存在巨大的冲突代价。

(2)具体参考:https://www.zhihu.com/question/55764216

Windows和pthread中提供的自旋锁的更多相关文章

  1. pthread中互斥量,锁和条件变量

    互斥量 #include <pthread.h> pthread_mutex_t mutex=PTHREAD_MUTEX_INTIIALIZER; int pthread_mutex_in ...

  2. Linux中自旋锁

    传统的spinlock Linux的的内核最常见的锁是自旋锁.自旋锁最多只能被一个可执行线程持有.如果一个执行线程试图获得一个被已经持有(争用)的自旋锁,那么该线程就会一直进行忙循环-旋转-等待锁重新 ...

  3. Linux内核同步:自旋锁

    linux内核--自旋锁的理解 自旋锁:如果内核配置为SMP系统,自旋锁就按SMP系统上的要求来实现真正的自旋等待,但是对于UP系统,自旋锁仅做抢占和中断操作,没有实现真正的“自旋”.如果配置了CON ...

  4. Optimistic concurrency control 死锁 悲观锁 乐观锁 自旋锁

    Optimistic concurrency control https://en.wikipedia.org/wiki/Optimistic_concurrency_control Optimist ...

  5. spinlock自旋锁de使用

    Linux内核中最常见的锁是自旋锁.一个自旋锁就是一个互斥设备,它只能有两个值:"锁定"和"解锁".如果锁可用,则"锁定"位被设置,而代码继 ...

  6. Nginx学习之四-Nginx进程同步方式-自旋锁(spinlock)

    自旋锁简介 Nginx框架使用了三种消息传递方式:共享内存.套接字.信号. Nginx主要使用了三种同步方式:原子操作.信号量.文件锁. 基于原子操作,nginx实现了一个自旋锁.自旋锁是一种非睡眠锁 ...

  7. JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁,

    如果需要查看具体的synchronized和lock的实现原理,请参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度) 在并发编程中,经常遇到多个线程访问同一个 ...

  8. Linux 同步方法剖析--内核原子,自旋锁和相互排斥锁

    在学习 Linux® 的过程中,您或许接触过并发(concurrency).临界段(critical section)和锁定,可是怎样在内核中使用这些概念呢?本文讨论了 2.6 版内核中可用的锁定机制 ...

  9. linux 自旋锁和信号量【转】

    转自:http://blog.csdn.net/xu_guo/article/details/6072823 版权声明:本文为博主原创文章,未经博主允许不得转载. 自旋锁最多只能被一个可执行线程持有( ...

随机推荐

  1. 《DSP using MATLAB》Problem 5.28

    昨晚手机在看X信的时候突然黑屏,开机重启都没反应,今天维修师傅说使用时间太长了,还是买个新的吧,心疼银子啊! 这里只放前两个小题的图. 代码: 1. %% ++++++++++++++++++++++ ...

  2. 初始if..else 条件语句

    if..else条件语句 如果我们希望有效的响应用户的输入,代码就需要具有判断能力.能够让程序进行判断的结构成为条件,条件判断语句返回的是布尔值真或假,真就执行一条线路,假就执行另外一条线路 if 条 ...

  3. Ubuntu下安装JDK图文教程详解 jdk-java6-30 .bin 的处理方法

    Ubuntu下安装JDK图文教程详解 jdk-java6-30 .bin 的处理方法: https://blog.csdn.net/mingjie1212/article/details/485250 ...

  4. PowerDesigner基础使用 ---- 入门学习

    1:入门级使用PowerDesigner软件创建数据库(直接上图怎么创建,其他的概念知识可自行学习) 我的PowerDesigner版本是16.5的,如若版本不一样,请自行参考学习即可.(打开软件即是 ...

  5. 拷贝某个区间(copy,copy_back)

    copy 为outputIterator中的元素赋值而不是产生新的元素,所以outputIterator不能是空的 如要元素安插序列,使用insert成员函数或使用copy搭配insert_itera ...

  6. idea+maven+springmvc+helloworld

    1.添加依赖,并在项目上添加Spring mvc框架的支持(add FrameWork Support): <dependency> <groupId>junit</gr ...

  7. java 泛型与通配符(?)

    泛型应用于泛型类或泛型方法的声明. 如类GenericTest public class GenericTest<T> { private T item; public void set( ...

  8. locate命令详解

    Linux locate命令 locate(locate) 命令用来查找文件或目录. locate命令要比find -name快得多,原因在于它不搜索具体目录,而是搜索一个数据库/var/lib/ml ...

  9. RedHat6.5安装单机flume1.6

    版本号: RedHat6.5   JDK1.8   apache-flume-1.6.0 1.apache-flume-1.6.0-bin.tar.gz 下载 官网下载地址:http://archiv ...

  10. R基于Bayes理论实现中文人员特性的性别判定

    参见 基于中文人员特征的性别判定方法  理论,告诉一个名字,来猜猜是男是女,多多少少有点算命的味道.此命题是一种有监督的学习方法,从标注好的训练数据学习到一个预测模型,然后对未标注的数据进行预测. 1 ...