.NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)
本随笔续接:.NET 同步与异步之锁(ReaderWriterLockSlim)(八)
之前的随笔已经说过、加锁虽然能很好的解决竞争条件,但也带来了负面影响:性能方面的负面影响。那有没有更好的解决方案呢?有,原子操作、即 Interlocked 这个类。
一、让我们先看一个计数的原子操作Demo
/// <summary>
/// 原子操作-计数
/// </summary>
public void Demo1()
{
Task.Run(() =>
{
long total = ;
long result = ; PrintInfo("正在计数"); Parallel.For(, , (i) =>
{
for (int j = ; j < ; j++)
{
Interlocked.Increment(ref total);
result++;
}
}); PrintInfo($"操作结果应该为\t\t: {10 * 10000000}");
PrintInfo($"原子操作结果\t\t: {total}");
PrintInfo($"i++操作结果\t\t: {result}");
});
}
原子操作-计数
由上述Demo可知、Interlocked 可以很好的保证 64位整型值的计数操作 能否符合预期,而普通的i++操作却出现了竞争条件。
Interlocked 对于整形操作提供的方法还是很多的,这里不多介绍了。
二、不一样的单例模式
Interlocked 中提供了 Interlocked.CompareExchange<T> 方法的泛型版本,让我们来看一下,这个泛型版本的一种巧妙的用法。
/// <summary>
/// 原子操作-单例模式
/// </summary>
public void Demo2()
{
ConcurrentQueue<InterlockedSingleClass> queue = new ConcurrentQueue<Demo.InterlockedSpinLockClass.InterlockedSingleClass>(); // 虽然这个测试不严谨、但也或多或少的说明了一些问题
for (int i = ; i < ; i++) // 同时分配的线程数过多、调度器反而调度不过来
{
Task.Run(() =>
{
var result = InterlockedSingleClass.SingleInstance; queue.Enqueue(result);
});
} // 1秒钟后显示结果
Task.Delay().ContinueWith((t) =>
{
PrintInfo($"利用原子操作-单例模式、生成的对象总数:{queue.Count}"); InterlockedSingleClass firstItem = null;
queue.TryDequeue(out firstItem); for (int i = ; i < queue.Count;)
{
InterlockedSingleClass temp = null;
queue.TryDequeue(out temp); if (temp == null || firstItem == null || !object.ReferenceEquals(temp, firstItem))
{
PrintInfo("单例模式失效");
}
} PrintInfo("原子操作-单例模式-运行完毕");
}); } public class InterlockedSingleClass
{
private static InterlockedSingleClass single = null; public static InterlockedSingleClass SingleInstance
{
get
{
// if (single == null) // 为了测试效果,该行代码注释掉
{
Interlocked.CompareExchange<InterlockedSingleClass>(ref single, new InterlockedSingleClass(), null);
} return single;
}
} }
原子操作-单例模式
针对Interlocked.CompareExchange<T>方法、我介绍两句:
1、第一个参数为 ref 参数,如果第一个参数 和 第三个参数的引用相等,则用第二个参数替换第一个参数的值,并将第一个参数的原始值返回。
2、该泛型方法 只接受类类型的参数。
三、自旋锁
自旋锁:提供一个相互排斥锁基元,在该基元中,尝试获取锁的线程将在重复检查的循环中等待,直至该锁变为可用为止。
/// <summary>
/// 自旋锁Demo,来源MSDN
/// </summary>
public void Demo3()
{
SpinLock sl = new SpinLock(); StringBuilder sb = new StringBuilder(); // Action taken by each parallel job.
// Append to the StringBuilder 10000 times, protecting
// access to sb with a SpinLock.
Action action = () =>
{
bool gotLock = false;
for (int i = ; i < ; i++)
{
gotLock = false;
try
{
sl.Enter(ref gotLock); sb.Append((i % ).ToString());
}
finally
{
// Only give up the lock if you actually acquired it
if (gotLock)
sl.Exit();
}
}
}; // Invoke 3 concurrent instances of the action above
Parallel.Invoke(action, action, action); // Check/Show the results
PrintInfo($"sb.Length = {sb.Length} (should be 30000)"); PrintInfo($"number of occurrences of '5' in sb: {sb.ToString().Where(c => (c == '5')).Count()} (should be 3000)"); }
自旋锁
看完了Demo,让我们再来深入了解一下自旋锁:
1、自旋锁本身是一个结构、而不是类,这样使用过多的锁时不会造成GC压力。
2、自旋锁是以一种循环等待的方式去尝试获取锁,也就是说、在等待期间 会一直占用CPU、如果等待时间过长会造成CPU浪费,而 Monitor会休眠(Sleep)。
3、自旋锁的使用准则:让临界区尽可能短(时间短)、非阻塞的方式。(因为等待时间过长会造成CPU浪费)
4、由于自旋锁是循环等待的方式、在执行方式上和Monitor的休眠不一样,自旋锁的执行速度会更快。而Monitor的休眠方式会造成额外的系统开销,执行速度反而会降低。
随笔暂告一段落、下一篇随笔按之前的目录顺序应该是介绍WaitHandler家族的, 笔者临时想变更下顺序、下一遍随笔:并发中的闭包。
附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip
参见更多:随笔导读:同步与异步
(未完待续...)
.NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)的更多相关文章
- Linux内核中锁机制之原子操作、自旋锁
很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实是由于操作系统中存在多进程对共享资源的并发访问,从而引起了进程间的竞态.这其中包括了我们所熟知的SMP系统,多 ...
- 大话Linux内核中锁机制之原子操作、自旋锁
转至:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其 ...
- 大话Linux内核中锁机制之原子操作、自旋锁【转】
转自:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实 ...
- Nginx学习之四-Nginx进程同步方式-自旋锁(spinlock)
自旋锁简介 Nginx框架使用了三种消息传递方式:共享内存.套接字.信号. Nginx主要使用了三种同步方式:原子操作.信号量.文件锁. 基于原子操作,nginx实现了一个自旋锁.自旋锁是一种非睡眠锁 ...
- 转:自旋锁(spinlock)
自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名. 由于 ...
- 抢占式内核与非抢占式内核中的自旋锁(spinlock)的差别
一.概括 (1)自旋锁适用于SMP系统,UP系统用spinlock是作死. (2)保护模式下禁止内核抢占的方法:1.运行终端服务例程时2.运行软中断和tasklet时3.设置本地CPU计数器preem ...
- 用户模式构造-简单自旋锁(SpinLock)
internal sealed class SimpleSpinLock { //0等于false(默认),1等于true ; public void Enter() { while (true) { ...
- .NET 同步与异步 之 警惕闭包(十)
本随笔续接:.NET 同步与异步 之 原子操作和自旋锁(Interlocked.SpinLock)(九) 至此.同步与异步 相关的常规操作(比较常见的操作).差不多已经介绍完毕. 本随笔就着重说一下闭 ...
- .NET同步与异步之相关背景知识(六)
在之前的五篇随笔中,已经介绍了.NET 类库中实现并行的常见方式及其基本用法,当然.这些基本用法远远不能覆盖所有,也只能作为一个引子出现在这里.以下是前五篇随笔的目录: .NET 同步与异步之封装成T ...
随机推荐
- Struts2(接受表单参数)请求数据自动封装和数据类型转换
Struts2请求数据自动封装: (1)实现原理:参数拦截器 (2)方式1:jsp表单数据填充到action中的属性: 普通的成员变量,必须给set,get可以不给的. 注意点,A ...
- 多线程中使用CheckForIllegalCrossThreadCalls = false访问窗口
在多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,如果需要访问窗口中的控件,可以在窗口构造函数中将CheckForIllegalCrossThreadCalls设置为 false publi ...
- 关于C#中”扩展方法必须在非泛型静态类中定义“问题的解决
问题描述: 在某个窗口下的编码中使用了以下扩展方法FindControl,以求根据字符串的值去操作控件(本文中的控件为Label控件)的属性. public static Control FindCo ...
- tarjan 算法求强连通分量
#include<bits/stdc++.h> #define ll long long using namespace std; const int P=1e6; ; ; const i ...
- 2018年商业版idea破解安装介绍
1. IntelliJ IDEA 2018商业版-安装 首先去官网http://www.jetbrains.com/idea/download/#section=windows下载Ultimate版( ...
- Redis数据结构之哈希
这个部分依旧分为两个步骤,一个是redis客户端,一个是java的客户端 一:Redis客户端 1.get与set 2.删除 3.增加 4.是否存在 hexists myhash2 age 5.获取长 ...
- Python 扩展技术总结(转)
一般来说,所有能被整合或导入到其他Python脚本中的代码,都可以称为扩展.你可以用纯Python来写扩展,也可以用C/C++之类的编译型语言来写扩展,甚至可以用java,C都可以来写 python扩 ...
- SpringMVC框架09——@ResponseBody的用法详解
@ResponseBody可以标注在方法上也可以标注在类上面.简单来说,当标注在方法上时,该方法的返回结果直接转成JSON格式:当标注在类上时,该类中的所有方法的返回结果都转换成JSON格式. 代码示 ...
- Solution for unable to create "dead-letter-exchange" in RabbitMQ
在参考 Dead-Letter-Exchange 进行Dead-letter-exchange的理解, 在本地时,想要创建 Dead-letter-exchange 时,一直报错,错误如下: Unha ...
- 洛谷P1198 [JSOI2008]最大数(BZOJ.1012 )
To 洛谷.1198 最大数 题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制:L不超过当 ...