概念

原子操作(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(基元、原语)的更多相关文章

  1. 【C#】C#线程_基元线程的同步构造

    目录结构: contents structure [+] 简介 为什么需要使用线程同步 线程同步的缺点 基元线程同步 什么是基元线程 基元用户模式构造和内核模式构造的比较 用户模式构造 易变构造(Vo ...

  2. 【C#进阶系列】28 基元线程同步构造

    多个线程同时访问共享数据时,线程同步能防止数据损坏.之所以要强调同时,是因为线程同步问题实际上就是计时问题. 不需要线程同步是最理想的情况,因为线程同步一般很繁琐,涉及到线程同步锁的获取和释放,容易遗 ...

  3. Clr Via C#读书笔记----基元线程同步构造

    线程文章:http://www.cnblogs.com/edisonchou/p/4848131.html 重点在于多个线程同时访问,保持线程的同步. 线程同步的问题: 1,线程同步比较繁琐,而且容易 ...

  4. 基元线程同步构造之 Mutes(互斥体)

    互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex)). 互斥体禁止多个线程同时进入受保护的代码“临界区”(critical section). 因 ...

  5. 基元线程同步构造之waithandle中 waitone使用

    在使用基元线程同步构造中waithandle中waitone方法的讲解: 调用waithandle的waitone方法阻止当前线程(提前是其状态为Nonsignaled,即红灯),直到当前的 Wait ...

  6. [.net]基元线程同步构造

    /* 基元线程同步构造 用户模式构造: 易变构造(Volatile Construct) 互锁构造(Interlocked Construct):自旋锁(Spinlock) 乐观锁(Optimisti ...

  7. .NET 中,编译器直接支持的数据类型称为基元类型(primitive type).基元类型和.NET框架类型(FCL)中的类型有直接的映射关系.

    .NET 中,编译器直接支持的数据类型称为基元类型(primitive type).基元类型和.NET框架类型(FCL)中的类型有直接的映射关系. The primitive types are Bo ...

  8. 解决c#所有单线程单元(STA)线程都应使用泵式等待基元(如 CoWaitForMultipleHandles),并在运行时间很长的操作过程中定期发送消息。 转载

    最近做一个后来程序,启动了事务后有一段操作业务,当运行一段时间后,出现这个异常 CLR 无法从 COM 上下文 0x1b1c38 转换为 COM 上下文 0x1b1da8,这种状态已持续 60 秒.拥 ...

  9. 单线程单元(STA)线程都应使用泵式等待基元

    CLR 无法从 COM 上下文 0x20ad98 转换为 COM 上下文 0x20af08,这种状态已持续 60 秒.拥有目标上下文/单元的线程很有可能执行的是非泵式等待或者在不发送 Windows ...

随机推荐

  1. 安卓无法访问Azure服务器和微软API

    Azure服务器保护机制限制移动端访问 必须使用移动app服务来转接api,才可以访问.

  2. elasticsearch算法之词项相似度算法(一)

    一.词项相似度 elasticsearch支持拼写纠错,其建议词的获取就需要进行词项相似度的计算:今天我们来通过不同的距离算法来学习一下词项相似度算法: 二.数据准备 计算词项相似度,就需要首先将词项 ...

  3. 使用3D Tiles Overview学习3D Tiles

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 3D Tiles的创建是为了在web上传输大量的3D数据集.作为 ...

  4. Redis的几点积累

    1.Redis比memcache快 Redis具有事务,持久化等机制,但是它还能做到高性能,原因包括如下: Libevent.和Memcached不同,Redis并没有选择libevent.Libev ...

  5. vue学习11-监听属性

    <!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta http ...

  6. gin中jsonp的用法

    package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := ...

  7. NGINX的动静分离;什么是负载均衡

    目录 一:动静分离 二:负载均衡 一:动静分离 动静分离是指在 web 服务器架构中,将静态页面与动态页面或者静态内容接口和动态内容接口分开不同系统访问的架构设计方法,进而提示整个服务的访问性和可维护 ...

  8. 如何在pyqt中在实现无边框窗口的同时保留Windows窗口动画效果(一)

    无边框窗体的实现思路 在pyqt中只要 self.setWindowFlags(Qt.FramelessWindowHint) 就可以实现边框的去除,但是没了标题栏也意味着窗口大小无法改变.窗口无法拖 ...

  9. TensorFlow 深度学习中文第二版·翻译完成

    原文:Deep Learning with TensorFlow Second Edition 协议:CC BY-NC-SA 4.0 不要担心自己的形象,只关心如何实现目标.--<原则>, ...

  10. 计算机电子书 2019 BiliDrive 备份

    下载方式 pip install BiliDriveEx bdex download <link> 链接 文档 链接 传智播客轻松搞定系列 C.C++.Linux.设计模式.7z (33. ...