一文带你了解.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).使用普通的互斥锁会涉及到操作系统的调度,因此小临界区一般首选自旋锁.自旋锁的工作方式 ...
随机推荐
- POJ2239简单二分匹配
题意: 一周有7天,每天可以上12节课,现在给你每科课的上课时间,问你一周最多可以上几科课,一科课只要上一节就行了. 思路: 简单题目,直接二分就行了,好久没写二分匹配了,练习 ...
- Django中图形验证码(django-simple-captcha)
django-simple-captcha 在网站开发的登录页面中,经常会需要使用到图形验证码来验证.在Django中,django-simple-captcha库包提供了图形验证码的使用. 下面我们 ...
- 5.PHP与Web页面交互
PHP与Web页面交互 PHP中提供了两种与Web页面交互的方法,一种是通过Web表单提交数据,另一种是通过URL参数传递. 表单提交用户名字和密码: <form name "form ...
- 6 JDBC
JDBC 理解图 需要mysql包 下载官网:https://downloads.mysql.com/archives/c-j/ 第一个JDBC项目 创建一个java项目,一路next 导入jar包 ...
- PHP基础—PHP的数据类型与常量使用
- IT培训软件测试怎么样,问问“过来人”!
经常看到有人在网上发帖子问:"XX培训(IT培训机构)怎么样,学过的老哥可以出来讲讲真话吗?"问这种问题的同学,来,站起来!我不得不在这儿说你两句:你要想知道一家IT培训机构到底怎 ...
- 检查dtd和Xschema文件限制下的xml文件是否符合的Java文件
先来xml文件: 1 <?xml version="1.0" encoding="utf-8"?> 2 <!DOCTYPE orders SY ...
- dynamic_cast和typeid
1. C++有三个支持RTTI的元素. 如果可能的话,dynamic_cast运算符将使用一个指向基类的指针来生成一个指向派生类的指针,否则,该运算符返回0--空指针. typeid运算符返回一个对t ...
- Visual Lab Online —— Alpha版本发布声明
Visual Lab Online -- Alpha版本发布声明 项目 内容 班级:北航2020春软件工程 博客园班级博客 作业:Alpha阶段发布声明 发布声明 目录 Visual Lab Onli ...
- Java堆的理解
堆的核心概述 所有的对象实例以及数组都应当在运行时分配在堆上 从实际实用角度看 --"几乎所有的对象实例都在堆中分配内存" 数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这 ...
