一文带你了解.Net自旋锁

本文主要讲解.Net基于Thread实现自旋锁的三种方式
基于Thread.SpinWait实现自旋锁
实现原理:基于Test--And--Set原子操作实现
使用一个数据表示当前锁是否已经被获取 0表示未被索取,1表示已经获取 获取锁时会将_lock的值设置为1 然后检查修改前的值是否等于0,
优点:
- 不使用Thread.SpinWait方法,重试的方法体会为空,CPU会使用它的最大性能来不断的进行赋值和比较指令,会浪费很大的性能,Thread.SpinWait提示CPU当前正在自旋锁的循环中,可以休息若干个时间周期
- 使用自旋锁需要注意的问题,自旋锁保护的代码应该在非常短的时间内执行完成,如果时间过长,其他线程不断重试导致影响其他线程进行
缺点:
- 当前实现没有考虑到公平性,如果多个线程同时获取锁失败,按时间顺序第一个获取锁的线程不一定会在释放锁后第一个获取成功,
代码实现:
public static class ThreadSpinWaitDemo
{
private static int _lock = 0;
private static int _counterA = 0;
private static int _counterB = 0;
public static void IncrementCounters()
{
while (Interlocked.Exchange(ref _lock, 1) != 0)
{
Thread.SpinWait(1);
}
++_counterA;
++_counterB;
Interlocked.Exchange(ref _lock, 0);
}
public static void GetCounters(out int counterA, out int counterB)
{
while (Interlocked.Exchange(ref _lock, 1) != 0)
{
Thread.SpinWait(1);
}
counterA = _counterA;
counterB = _counterB;
Interlocked.Exchange(ref _lock, 0);
}
}
基于SpinWaite实现自旋锁
特性是SpinOnce方法的次数,如果在一定次数以内并且当前逻辑核心所大于1,则调用Thread.SpinWait函数;如果超过一定次数或者当前环境逻辑核心数等于1,则交替使用
Thread.Sleep(0)和Thread.Yield函数,表示切换到其他线程,如果再超过一定次数,则让当前线程休眠
SpinWaite解决Thread.SpinWait中的两个问题
- 如果自旋锁运行时间超长,SpinWaite可以提示操作系统切换到其他线程或者让当前线程进入休眠状态,
- 如果当前环境只有一个核心逻辑,SpinWaite不会执行Thread.SpinWait函数,而是直接提示操作系统切换到其他线程,
public static class ThreadSpinOnceDemo
{
private static int _lock = 0;
private static int _counterA = 0;
private static int _counterB = 0;
public static void IncrementCounters()
{
var spinWait = new SpinWait();
while (Interlocked.Exchange(ref _lock, 1) != 0)
{
spinWait.SpinOnce();
}
++_counterA;
++_counterB;
Interlocked.Exchange(ref _lock, 0);
}
public static void GetCounters(out int counterA, out int counterB)
{
var spinWait = new SpinWait();
while (Interlocked.Exchange(ref _lock, 1) != 0)
{
spinWait.SpinOnce();
}
counterA = _counterA;
counterB = _counterB;
Interlocked.Exchange(ref _lock, 0);
}
}
基于SpinLock实现自旋锁
封装了SpinWaite的逻辑
SpinLock代码实现
public class ThreadSpinLockDemo
{
private static SpinLock _spinLock = new SpinLock();
private static int _counterA = 0;
private static int _counterB = 0;
public static void IncrementCounters()
{
bool lockTaken = false;
try
{
_spinLock.Enter(ref lockTaken);
++_counterA;
++_counterB;
}
finally
{
if (lockTaken)
{
_spinLock.Exit();
}
}
}
public static void GetCounters(out int counterA, out int counterB)
{
bool lockTaken = false;
try
{
_spinLock.Enter(ref lockTaken);
counterA = _counterA;
counterB = _counterB;
}
finally
{
if (lockTaken)
{
_spinLock.Exit();
}
}
}
}
简述 Thread.Sleep(0)和Thread.Yield的区别
- 在Windows系统中 Thread.Sleep调用系统提供的SleepEx函数,Thread.Yield函数调用的是系统提供的SwitchToThread方法,
- 区别在于SwitchToThread函数只会切换到当前核心逻辑关联的待运行队列的线程,不会切换到其他核心逻辑关联的线程上,而SleepEx函数会切换到任意逻辑核心关联的待运行队列中的线程,并且让当前线程在指定时间内无法重新进入待运行队列(如果线程为0 那么线程可以立刻重新进入待运行队列)
- 在Linux和OSX中 Thread.Sleep函数在休眠时间不为0时会调用pthread类库提供的pthread_cond_timedWait函数,在休眠时间为0时会调用sched_yield函数,Thread.Yield同样会调用sched_yield函数 sched_yield在windows和osx系统中没有区别,都只会切换到当前和逻辑核心关心的待运行队列中的线程,不会切换到其他核心逻辑关联的线程上。在unix系统上调用系统提供的sleep函数并传入0 会直接忽略返回
本文基于.Net Core底层入门总结内容
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧
个人微信
一文带你了解.Net自旋锁的更多相关文章
- 一文带你了解.Net互斥锁
本文主要讲解.Net基于Threading.Mutex实现互斥锁 基础互斥锁实现 基础概念:和自旋锁一样,操作系统提供的互斥锁内部有一个数值表示锁是否已经被获取,不同的是当获取锁失败的时候,它不会反复 ...
- 一文带你剖析LiteOS互斥锁Mutex源代码
摘要:多任务环境下会存在多个任务访问同一公共资源的场景,而有些公共资源是非共享的临界资源,只能被独占使用.LiteOS使用互斥锁来避免这种冲突,互斥锁是一种特殊的二值性信号量,用于实现对临界资源的独占 ...
- linux内核--自旋锁的理解
http://blog.chinaunix.net/uid-20543672-id-3252604.html 自旋锁:如果内核配置为SMP系统,自旋锁就按SMP系统上的要求来实现真正的自旋等待,但是对 ...
- 深入分析Linux自旋锁【转】
转自:http://blog.chinaunix.net/uid-20543672-id-3252604.html 前言: 在复习休眠的过程中,我想验证自旋锁中不可休眠,所以编写了一个在自旋锁中休眠的 ...
- Linux内核同步:自旋锁
linux内核--自旋锁的理解 自旋锁:如果内核配置为SMP系统,自旋锁就按SMP系统上的要求来实现真正的自旋等待,但是对于UP系统,自旋锁仅做抢占和中断操作,没有实现真正的“自旋”.如果配置了CON ...
- 深入分析Linux自旋锁
原创 2016-08-12 tekkamanninja CU技术社区 作者| tekkamanninja本文版权由tekkamanninja所有,如需转载,请联系本公众号获取授权!在复习休眠的过程 ...
- 一文读懂原子操作、内存屏障、锁(偏向锁、轻量级锁、重量级锁、自旋锁)、Disruptor、Go Context之上半部分
我不想卷,我是被逼的 在做了几年前端之后,发现互联网行情比想象的差,不如赶紧学点后端知识,被裁之后也可接个私活不至于饿死.学习两周Go,如盲人摸象般不知重点,那么重点谁知道呢?肯定是使用Go的后端工程 ...
- 一文带你.Net混合锁和lock语句
本文主要讲解.Net基于Monitor.Enter和lock实现互斥锁 Monitor.Enter实现 相比前面的锁来说,混合锁的性能更高,任何引用类型的对象都可以做为锁对象,不需要事先创建指定类型的 ...
- MCS锁——可伸缩的自旋锁
在编写并发同步程序的时候,如果临界区非常小,比如说只有几条或几十条指令,那么我们可以选择自旋锁(spinlock).使用普通的互斥锁会涉及到操作系统的调度,因此小临界区一般首选自旋锁.自旋锁的工作方式 ...
随机推荐
- Andrew Ng机器学习算法入门(三):线性回归算法
线性回归 线性回归,就是能够用一个直线较为精确地描述数据之间的关系.这样当出现新的数据的时候,就能够预测出一个简单的值. 线性回归中最常见的就是房价的问题.一直存在很多房屋面积和房价的数据,如下图所示 ...
- canvas绘制折线路径动画
最近有读者加我微信咨询这个问题: 其中的效果是一个折线路径动画效果,如下图所示: 要实现以上路径动画,一般可以使用svg的动画功能.或者使用canvas绘制,结合路径数学计算来实现. 如果用canva ...
- 【python】Leetcode每日一题-二叉搜索迭代器
[python]Leetcode每日一题-二叉搜索迭代器 [题目描述] 实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器: BSTIterator(T ...
- axios提交表单
后端使用@RequestBody接收jsons数据 因为后端接收json数据,所以前端也要发送json 项目的前端是使用layui的数据表单 案例方法 方法一:JSON字符串 提交的数据格式 {&qu ...
- 一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第n次落地时,共经过多少米?第n次反弹多高?(n<=10)
单纯考逻辑 题目: 一球从100米高度自由落下,每次落地后反跳回原高度的一半:再落下,求它在第n次落地时,共经过多少米?第n次反弹多高?(n<=10) 输入描述: 一行,一个整数n (1< ...
- 软负载Nginx和硬负载F5的优缺点对比
对于数据流量过大的网络中,往往单一设备无法承担,需要多台设备进行数据分流,而负载均衡器就是用来将数据分流到多台设备的一个转发器. a.软件负载均衡解决方案 在一台服务器的操作系统上,安装一个附加软件 ...
- Tomcat&Http协议-授课
1 企业开发简介 1.1 JavaEE规范 JavaEE规范是J2EE规范的新名称,早期被称为J2EE规范,其全称是Java 2 Platform Enterprise Edition,它是由SUN公 ...
- 【Cocos2d-x】屏蔽Emoji并解决由于Emoji导致的崩溃问题
IOS的Emoji表情因为编码问题,在Android手机上无法正常显示,如果当前的cc.Label节点使用的是系统字,在系统字库中找不到对应编码的字符,会导致崩溃. 为了解决这个问题,又要兼顾新老版本 ...
- [Qt] Librecad 源码分析
libraries jwwlib libdxfrw:一个免费的C++库,用于读写文本格式或二进制格式的DXF文件(C++ library to read and write DXF/DWG files ...
- Select Screen 0 with xrandr Ask QuestionScreen 0" here describes your whole virtual display made of these two outputs: eDP-1-
Screen 0" here describes your whole virtual display made of these two outputs: eDP-1-1: physica ...
