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. 【HDOJ2586】【Tarjan离线求LCA】

    http://acm.hdu.edu.cn/showproblem.php?pid=2586 How far away ? Time Limit: 2000/1000 MS (Java/Others) ...

  2. MySql中的一些小坑

    1. mysql启动时,若使用mysqld_safe的方式启动服务,需要使用mysqladmin shutdown的方式来停止服务. 若使用mysqld shutdown的方式停止服务,有可能会出现如 ...

  3. centos Cannot allocate memory for the buffer pool

    mysql 无法启动 ,查看日志: --01T15::.401599Z [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. P ...

  4. 如何取消noarch.rpm包

    有一次部署zabbix服务器,不小心rpm -ivh zabbix的el7版本的rpm了,但是我的系统是centos6.5的,所以就尴尬了 rpm -ivh http://repo.zabbix.co ...

  5. terraform plugin 版本以及changlog 规范

    文章来自官方文章,转自:https://www.terraform.io/docs/extend/best-practices/versioning.html 里面包含了版本命名的规范,以及chang ...

  6. Compoxure 微服务组合proxy 中间件

    Compoxure 是一个不错的微服务组合中间件,使用此工具我们可以快速的进行micro frontends 应用的开发 使用此工具我们可以替换esi+ ssi 的开发模型(尽管都挺不错). 同时支持 ...

  7. STDOUT/STDERR重定向到ALOG中

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/kangear/article/details/24534707      说下背景:如今众多Andr ...

  8. Array.asList()

    package study.stage2;import java.util.*; /** * Created by Sandy.Liu on 2017/7/19. */public class Asl ...

  9. 3d tech

    product Company    3D "smart cameras" such as the Gocator LMI Technologies (Delta, BC Cana ...

  10. redis 学习资料

    redis 学习资料 网址 Redis 教程(菜鸟教程) http://www.runoob.com/redis/redis-tutorial.html Redis 命令参考 http://redis ...