本文主要讲解.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自旋锁的更多相关文章

  1. 一文带你了解.Net互斥锁

    本文主要讲解.Net基于Threading.Mutex实现互斥锁 基础互斥锁实现 基础概念:和自旋锁一样,操作系统提供的互斥锁内部有一个数值表示锁是否已经被获取,不同的是当获取锁失败的时候,它不会反复 ...

  2. 一文带你剖析LiteOS互斥锁Mutex源代码

    摘要:多任务环境下会存在多个任务访问同一公共资源的场景,而有些公共资源是非共享的临界资源,只能被独占使用.LiteOS使用互斥锁来避免这种冲突,互斥锁是一种特殊的二值性信号量,用于实现对临界资源的独占 ...

  3. linux内核--自旋锁的理解

    http://blog.chinaunix.net/uid-20543672-id-3252604.html 自旋锁:如果内核配置为SMP系统,自旋锁就按SMP系统上的要求来实现真正的自旋等待,但是对 ...

  4. 深入分析Linux自旋锁【转】

    转自:http://blog.chinaunix.net/uid-20543672-id-3252604.html 前言: 在复习休眠的过程中,我想验证自旋锁中不可休眠,所以编写了一个在自旋锁中休眠的 ...

  5. Linux内核同步:自旋锁

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

  6. 深入分析Linux自旋锁

    原创 2016-08-12 tekkamanninja CU技术社区   作者| tekkamanninja本文版权由tekkamanninja所有,如需转载,请联系本公众号获取授权!在复习休眠的过程 ...

  7. 一文读懂原子操作、内存屏障、锁(偏向锁、轻量级锁、重量级锁、自旋锁)、Disruptor、Go Context之上半部分

    我不想卷,我是被逼的 在做了几年前端之后,发现互联网行情比想象的差,不如赶紧学点后端知识,被裁之后也可接个私活不至于饿死.学习两周Go,如盲人摸象般不知重点,那么重点谁知道呢?肯定是使用Go的后端工程 ...

  8. 一文带你.Net混合锁和lock语句

    本文主要讲解.Net基于Monitor.Enter和lock实现互斥锁 Monitor.Enter实现 相比前面的锁来说,混合锁的性能更高,任何引用类型的对象都可以做为锁对象,不需要事先创建指定类型的 ...

  9. MCS锁——可伸缩的自旋锁

    在编写并发同步程序的时候,如果临界区非常小,比如说只有几条或几十条指令,那么我们可以选择自旋锁(spinlock).使用普通的互斥锁会涉及到操作系统的调度,因此小临界区一般首选自旋锁.自旋锁的工作方式 ...

随机推荐

  1. 用户模式下的线程同步的分析(Windows核心编程)

    线程同步 同一进程或者同一线程可以生成许多不同的子线程来完成规定的任务,但是多个线程同时运行的情况下可能需要对某个资源进行读写访问,比如以下这个情况:创建两个线程对同一资源进行访问,最后打印出这个资源 ...

  2. springboot添加操作

    更多精彩关注微信公众号 Mybaits技术连接数据库 resources #update tomcat port server.port=8888 #config datasource(mysql) ...

  3. ThreadLocal不好用?那是你没用对!

    在 Java 中,如果要问哪个类使用简单,但用好最不简单?我想你的脑海中一定会浮现出一次词--"ThreadLocal". ​ 确实如此,ThreadLocal 原本设计是为了解决 ...

  4. python多进程、多线程服务器和客户端的简单实现

    使用了多进程的服务器: from SocketServer import TCPServer, ForkingMixIn, ThreadingMixIn, StreamRequestHandler c ...

  5. dynamic_cast和typeid

    1. C++有三个支持RTTI的元素. 如果可能的话,dynamic_cast运算符将使用一个指向基类的指针来生成一个指向派生类的指针,否则,该运算符返回0--空指针. typeid运算符返回一个对t ...

  6. [bug] Maven项目缺少Maven Dependencies

    参考 https://blog.csdn.net/whitemiao/article/details/90177135

  7. Ansible_处理失败的任务

    一.Ansible处理任务失败 1.管理play中任务错误 1️⃣:Ansible评估任务的返回代码,从而确定任务是成功还是失败 2️⃣:通常而言,当任务失败时,Ansible将立即在该主机上中止pl ...

  8. nginx location标签的匹配规则

    location的匹配 匹配符 匹配规则 优先级 = 精确匹配 1 ^~ 以某个字符串开头 2 ~ 区分大小写的正则匹配 3 ~* 不区分大小写的正则匹配 4 !~ 区分大小写不匹配的正则 5 !~* ...

  9. Sqli-labs-master通关解析(持续更新中。。。)

    大多情况下:SQL注入其实就是构造正确的mysql命令,让网页回显本不应该让我们看到的数据(如用户的账号和密码). 第一关-联合查询注入 查库 // 查看当前页面在的数据库 ?id=-1' union ...

  10. FD_SET -(转自 kakaxia6337的专栏)

    FD_ZERO,FD_ISSET这些都是套节字结合操作宏 看看MSDN上的select函数, 这是在select   io   模型中的核心,用来管理套节字IO的,避免出现无辜锁定. int   se ...