【C# 线程】 atomic action原子操作|primitive(基元、原语)
概念
原子操作(atomic action):也叫primitive(原语、基元),它是操作系统用语范畴。指由若干条指令组成的,用于完成一定功能的一个过程。 原语是由若干个机器指令构成的完成某种特定功能的一段程序,具有不可分割性·即原语的执行必须是连续的,在执行过程中不允许被中断。
操作系统只需在执行以下操作时暂时屏蔽全部中断:测试信号量、更新信号量以及在需要时使某个进程睡眠。由于这些动作只需要几条指令,所以屏蔽中断不会带来什么副作用。如果使用多个CPU,则每个信号量应由一个锁变量进行保护。
在.net中实现原子操作的类是 Interlocked类。CAS在.NET中的实现类是Interlocked
Interlocked类主要方法
interlocked是基于CAS操作
CAS操作基于CPU提供的原子操作指令实现。只能保证共享变量操作的原子:
对于Intel X86处理器,可通过在汇编指令前增加LOCK前缀来锁定系统总线,使系统总线在汇编指令执行时无法访问相应的内存地址。而各个编译器根据这个特点实现了各自的原子操作函数。
CAS是一种有名的无锁算法。无锁编程(指C#代码中不加锁,汇编代码会自动加锁),即不适用锁的情况下实现多线程之间的变量同步,也就是在没有现成被阻塞的情况下实现变量的同步。
CompareExchange(ref a ,b,c):比较吧a,c是否相等,如果相等,则用b替换a的值。
CompareExchange<T>(T, T, T) 比较两个指定的引用类型的实例 T 是否相等,如果相等,则替换第一个。好多原子操作都是基于这个函数实现的。
Decrement(): 安全递减1,相当于 i--
Exchange(): 安全交换数据,相当于 a = 30
Increment() :安全递加1,相当于 i++
Add() :安全相加一个数值,相当于 a = a + 3
Read() : 安全读取数值,相等于int a=b
案例:用5个线程从0数到1亿
using System.Diagnostics; class Program
{
static long counter = 1;
/// <summary>
/// 开5个线程 从0数到1亿
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Parallel.Invoke(f1, f1, f1, f1, f1);
// f1();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Console.WriteLine(counter);
}
static void f1()
{ for (int i = 1; i <= 10_000_000; i++)
{
Interlocked.Increment(ref counter);
// counter++;
}
} }
//5个线程花费时间 1608ms //单线程 125 ms
本以为5个线程会更快,结果还不如一个线程快。这是什么问题???
因为Interlocked.Increment是采用CAS操作
CAS是一种有名的无锁算法。无锁编程(指编程语言方面),即不适用锁的情况下实现多线程之间的变量同步,也就是在没有现成被阻塞的情况下实现变量的同步。
CAS原理
CAS,是“Compare And Swap”的缩写,中文简称就是“比较并替换”。
在这个机制中有三个核心的参数:
- 主内存中存放的共享变量的值:V(一般情况下这个V是内存的地址值,通过这个地址可以获得内存中的值)
- 工作内存中共享变量的副本值,也叫预期值:A
- 需要将共享变量更新到的最新值:B
如上图中,主存中保存V值,线程中要使用V值要先从主存中读取V值到线程的工作内存A中,然后计算后变成B值,最后再把B值写回到内存V值中。多个线程共用V值都是如此操作。CAS的核心是在将B值写入到V之前要比较A值和V值是否相同,如果不相同证明此时V值已经被其他线程改变,重新将V值赋给A(自旋),并重新计算得到B,如果相同,则将B值赋给V。
值得注意的是CAS机制中的这步步骤是原子性的(从cpu指令层面提供的原子操作),所以CAS机制可以解决多线程并发编程对共享变量读写的原子性问题。
CAS的适用场景
读多写少:如果有大量的写操作,CPU开销可能会过大,因为冲突失败后会不断重试(自旋),这个过程中会消耗CPU
cas操作多出比较和写入内存,所以要耗费太多时间了。而单线程不用Interlocked 直接用缓存的数据进行累加,所以单线程更快。
【C# 线程】 atomic action原子操作|primitive(基元、原语)的更多相关文章
- 【C#】C#线程_基元线程的同步构造
目录结构: contents structure [+] 简介 为什么需要使用线程同步 线程同步的缺点 基元线程同步 什么是基元线程 基元用户模式构造和内核模式构造的比较 用户模式构造 易变构造(Vo ...
- 【C#进阶系列】28 基元线程同步构造
多个线程同时访问共享数据时,线程同步能防止数据损坏.之所以要强调同时,是因为线程同步问题实际上就是计时问题. 不需要线程同步是最理想的情况,因为线程同步一般很繁琐,涉及到线程同步锁的获取和释放,容易遗 ...
- Clr Via C#读书笔记----基元线程同步构造
线程文章:http://www.cnblogs.com/edisonchou/p/4848131.html 重点在于多个线程同时访问,保持线程的同步. 线程同步的问题: 1,线程同步比较繁琐,而且容易 ...
- 基元线程同步构造之 Mutes(互斥体)
互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex)). 互斥体禁止多个线程同时进入受保护的代码“临界区”(critical section). 因 ...
- 基元线程同步构造之waithandle中 waitone使用
在使用基元线程同步构造中waithandle中waitone方法的讲解: 调用waithandle的waitone方法阻止当前线程(提前是其状态为Nonsignaled,即红灯),直到当前的 Wait ...
- [.net]基元线程同步构造
/* 基元线程同步构造 用户模式构造: 易变构造(Volatile Construct) 互锁构造(Interlocked Construct):自旋锁(Spinlock) 乐观锁(Optimisti ...
- .NET 中,编译器直接支持的数据类型称为基元类型(primitive type).基元类型和.NET框架类型(FCL)中的类型有直接的映射关系.
.NET 中,编译器直接支持的数据类型称为基元类型(primitive type).基元类型和.NET框架类型(FCL)中的类型有直接的映射关系. The primitive types are Bo ...
- 解决c#所有单线程单元(STA)线程都应使用泵式等待基元(如 CoWaitForMultipleHandles),并在运行时间很长的操作过程中定期发送消息。 转载
最近做一个后来程序,启动了事务后有一段操作业务,当运行一段时间后,出现这个异常 CLR 无法从 COM 上下文 0x1b1c38 转换为 COM 上下文 0x1b1da8,这种状态已持续 60 秒.拥 ...
- 单线程单元(STA)线程都应使用泵式等待基元
CLR 无法从 COM 上下文 0x20ad98 转换为 COM 上下文 0x20af08,这种状态已持续 60 秒.拥有目标上下文/单元的线程很有可能执行的是非泵式等待或者在不发送 Windows ...
随机推荐
- [一]Cesium利其器——Visual Studio Code
Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ IDE Web前端刚入门的朋友,常常想选择一个快速.好用.流行( ...
- Spring boot + Vue axios 文件下载
后端代码: @GetMapping("/{sn}") @ApiOperation(value = "获取文件",notes = "获取文件" ...
- C++线程基础笔记(一)
标准写法: #include<iostream> #include<thread> using namespace std; void MyThread() { cout &l ...
- 什么是Filter过滤器
一,什么是Filter过滤器: JavaWeb三大组件之一 Filter过滤器是JavaEE的规范.也就是接口 Filter过滤器作用:拦截请求,过滤响应 拦截请求常见的应用场景有: 1,权限检查 2 ...
- web.xml最新配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmln ...
- linux字符编码防止乱码
一:linux字符编码 en_US.UTF-8 : 美式英文,utf-8 zh_CN.UTF-8 临时优化 export LANG=zh_CN.UTF-8 : 设置编码 永久优化 vim /etc/l ...
- Protobuf 动态加载 .pb 文件并操作 Message
之前写了<Protobuf 动态加载 .proto 文件并操作 Message>.除了直接读取 .proto 文件之外,还有一种类似的方法.先把 .proto 文件编译成 .pb 文件,再 ...
- 如何在Visual Studio中添加opencvsharp的可视化工具
这个文件放到这个目录下,就可以看mat对象查看了
- Network Kit与三七游戏共创流畅游戏体验,无惧网络延迟
本文分享于HMS Core联盟论坛<[开发者说]无惧高网络吞吐量,HMS Core Network Kit与三七游戏共创流畅游戏体验>的采访整理. 三七游戏拥有<斗罗大陆·魂师对决& ...
- Redis 常见面试题(2020最新版)
阶段汇总集合:001期~180期汇总,方便阅读(增加设计模式) 概述 什么是Redis Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD许可) ...