最近看并行编程书本的一些心得,简单记录下多线程和并行编程必知必会的几个概念,再次加深自己的理解。

.NET Framework4提供了一个新的命名空间System.Collections.Concurrent用于解决常用集合在并发情况下的线程安全问题(ps:通过这个命名空间还可以访问用于并行化循环和PLINQ的自定义分区器Partitioner)。这个命名空间下的所有线程安全集合都在某种程度上使用了无锁技术。也就是说,这些集合通过使用比较并交换(Compare And Swap,CAS)指令和内存屏障(Memory Barrier),避免了使用典型的互斥的重量级的锁,虽然实际开发中对性能要求不高的业务系统中加锁可以获得最经济实惠的开发效益。

一、锁

在多线程和并发这两个主题下从来都离不开锁的身影,锁是用来做并发最简单但是代价也可能是最高的方式,在某些场景下加锁是非常经济实惠的解决方案。

锁很好用,代码也极好维护,但是必须清楚不能滥用,否则可能导致严重性能问题。因为加锁会增加系统内核态与用户态之间的切换开销以及线程调度开销。我们知道,加锁、释放锁会导致上下文切换和调度延时,等待锁的线程会被挂起直至锁释放。内核态的锁需要操作系统进行一次上下文切换,在上下文切换的时候,cpu之前缓存的指令和数据都将失效,对性能影响最大。

1、使用锁的三大基本原则

a、不使用锁

b、使用小粒度的锁,常见的锁如互斥锁(lock)、读写锁(ReaderWriterLockSlim,ReaderWriterLock)等等

c、锁住尽可能短的时间

2、同步对象

为了封装锁的逻辑,通常需要一个同步对象。比如常见的简单粗暴的同步代码里,lock(sth)的sth就是一个同步对象。

同步对象必须是引用类型(字符串通常不适合做同步对象,想想为什么),而且它通常是私有的,通常是一个instance或者static field。

为了精确的控制锁的scope和粒度,我们通常会创建一个dedicated字段,比如locker,asyncObj等;

避免使用lock(this) 或者lock(typeof(sometype))或者lock(string),这种使用方法将无法封装锁的逻辑,难以避免死锁和过度的阻塞,甚至在一个进程内还会溢出app domain边界。

3、如何减少锁?

如果你的设计为了复用而有很多共享数据,那么在多线程高并发环境下使用Lock还是LockFree的同步算法都是不可避免的。

根据经验,当我们需要访问共享的可写字段时,通常就可以通过锁来同步。

为了减少锁,我们需要减少共享数据的使用。

二、CAS

1、基本原理

CAS,简单说来就是比较并交换,大致逻辑就是如果A与B相等,那么将C赋值给A。

CAS 操作包含三个操作数:内存位置(V)、预期原值(A)和新值(B)。

如果内存位置的值V与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B;否则,处理器不做任何操作。

2、内部实现伪代码

bool CAS(T* ptr, T expected, T fresh)
{if(*ptr != expected)return false;*ptr = fresh;return true;
}

3、优点

CAS是CPU指令级的操作,看上去只有一步原子操作,避免了请求操作系统来裁定锁的问题,所以一般很快。CAS操作是基于共享数据不会被修改的假设,当同步冲突出现的机会很少时,这种假设就能带来较大的性能提升。主要优点如下:

a、避免通常加锁所导致的严重性能开销,减少了内核态与用户态之间的切换开销以及线程调度开销;

b、实现更细力度的并行控制,提高系统吞吐量,有些情况下可以达到成倍的关键业务的性能提升。

4、缺点

CAS虽然有明显的优点,但天下没有免费的午餐 ,通过CAS实现的LockFree也存在很多问题,比如:
a、与硬件体系结构的内存读写模型相关,所以存在移植问题
b、实现复杂,其正确性很难被证明
    (a)、受限于CPU指令
    (b)、即使简单的数据结构也要通过复杂的算法来实现
    (c)、ABA问题
c、代码难以维护
d、存在活锁(livelock)问题

所谓活锁,简单来讲就是指事物1可以使用资源,但它让其他事物先使用资源;事物2可以使用资源,但它也让其他事物先使用资源,于是两者一直谦让,结果两者都无法使用资源。

三、MemoryBarrier

为什么需要MemoryBarrier(内存屏蔽),MSDN的解释是:

MemoryBarrier is required only on multiprocessor systems with weak memory ordering (for example, a system employing multiple Intel Itanium processors).

Synchronizes memory access as follows: The processor executing the current thread cannot reorder instructions in such a way that memory accesses prior to the call to MemoryBarrier execute after memory accesses that follow the call to MemoryBarrier.

简单来说就是多核处理器会对运行CPU指令顺序重排优化,而编译后的程序可能因为编译器优化或者计算机硬件结构比如分布式系统等诸多原因,不以编码时的顺序执行,从而引发预期外的问题。

Memory Barrier就是一种在底层保证语句按顺序执行的解决方案,调用Thread.MemoryBarrier()之后的代码中内存访问不能在这之前就完成了,也就是它可以限制指令重排和内存读写的缓存。

参考:

http://stackoverflow.com/questions/3556351/why-we-need-thread-memorybarrier

Barrier类,允许多个任务同步它们不同阶段上的并发工作。

四、并行集合

System.Collections.Concurrent命名空间下主要的线程安全并行集合有如下几种:

1、ConcurrenctQueue<T>

ConcurrenctQueue是System.Collections.Queue的并发版本。它是一个FIFO(Fisrt In,First Out,先进先出)的集合。

ConcurrenctQueue是完全无锁的,但是当CAS操作失败且面临资源争用的时候,它可能会自旋并且进行重试操作。

2、ConcurrenctStack<T>

ConcurrenctStack是System.Collections.Stack的并发版本。它是一个LIFO(Lastt In,First Out,后进先出)的集合。

ConcurrenctStack是完全无锁的,但是当CAS操作失败且面临资源争用的时候,它可能会自旋并且进行重试操作。

3、ConcurrenctBag<T>

ConcurrenctBag提供了一个无序的对象集合,而且支持对象重复,当不用考虑顺序时非常有用。

ConcurrenctBag使用了很多不同的机制,最大程度地减少了同步的需求以及同步所带来的开销。

ConcurrenctBag为每一个访问集合的线程维护了一个本地队列,而且在可能的情况下,它会以无锁的方式访问这个本地队列。

ConcurrenctBag在同一个线程添加元素(生产)和删除元素(消费)的场合下效率非常高。然而,ConcurrenctBag有时候会用到锁,因此,在生产者和消费者线程完全分开的场景下效率非常低下。

4、ConcurrenctDictionary<TKey,TValue>

ConcurrenctDictionary与经典的键值对的字典类似,提供了并发的键值访问。它是System.Collections.IDictionary实现的并发版本。

ConcurrenctDictionary对于读操作是完全无锁的,它对于需要频繁使用读取的操作进行了优化。

当很多任务或者线程在字典中添加或者修改数据的时候,ConcurrenctDictionary会使用细粒度的锁。

5、 BlockingCollection<T>

BlockingCollection与经典的阻塞队列数据结构类似,它是对一个IProducerConsumerCollection<T>实例的包装器,提供了阻塞(block)和限界(bound)的能力。

BlockingCollection能够适用于有多个任务添加和删除数据的生产者-消费者的情形。

这里再顺带提一下IProducerConsumerCollection<T>这个接口,它继承自IEnumerable<T>、ICollection和IEnumerable。忍不住要为IProducerConsumerCollection<T>、IEnumerable<T>、ICollection和IEnumerable这几个接口的抽象拍手叫好。可以说,MS对集合的设计是非常富有远见并适应变化的。

PS:关于并行集合和线程安全,很久之前我也写过总结,可以参考之前的拙文浅析线程安全容器的实现。

参考:

<<C#并行编程高级教程>>

http://msdn.microsoft.com/zh-cn/library/system.collections.concurrent(v=vs.110).aspx

http://msdn.microsoft.com/zh-cn/library/dd267312(v=vs.110).aspx

http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html

Lock,LockFree,MemoryBarrier,ConcurrentCollection相关推荐

  1. 多线程下Lock、Level-Lock、Lock-Free、ReaderWriterLock、ReaderWriterLockSlim性能比较

    多线程环境下使用那种锁往往凭个人感觉,缺乏测试数据的支持很容易走入误区,就像我知道的很多人就觉得Lock好慢好慢,Lock-Free 就能飞起来一样. 下面对Lock.Level-Lock.Lock- ...

  2. 下篇 | 说说无锁(Lock-Free)编程那些事(下)

    6 内存屏障(Memory Barriers) 6.1 What Memory Barriers? 内存屏障,也称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的 ...

  3. Lock-Free 编程

    文章索引 Lock-Free 编程是什么? Lock-Free 编程技术 读改写原子操作(Atomic Read-Modify-Write Operations) Compare-And-Swap 循 ...

  4. lockfree buffer test

    性能测试(3): 对无锁队列boost::lockfree::queue和moodycamel::ConcurrentQueue做一个性能对比测试 版权声明:本文为博主zieckey原创文章,转载时请 ...

  5. [转] Lock-Free 数据结构

    锁无关的(Lock-Free)数据结构 在避免死锁的同时确保线程继续 http://blog.csdn.net/pongba/archive/2006/01/26/588638.aspx Andrei ...

  6. 上篇 | 说说无锁(Lock-Free)编程那些事

    1. 引言 现代计算机,即使很小的智能机亦或者平板电脑,都是一个多核(多CPU)处理设备,如何充分利用多核CPU资源,以达到单机性能的极大化成为我们码农进行软件开发的痛点和难点.在多核服务器中,采用多 ...

  7. JAVA lock 原理讲解

    Lock完全用Java写成,在java这个层面是无关JVM实现的. 在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock.ReadWri ...

  8. 关于 lockfree 算法

    lockfree的本质是乐观锁.也就是说,它假设多数情况下,别人不会改变.一个通用的lockfree算法可描述如下:   lockfree_modify(DataT* data) {     for ...

  9. 【转】细说.NET中的多线程 (六 使用MemoryBarrier,Volatile进行同步)

    上一节介绍了使用信号量进行同步,本节主要介绍一些非阻塞同步的方法.本节主要介绍MemoryBarrier,volatile,Interlocked. MemoryBarriers 本文简单的介绍一下这 ...

最新文章

  1. OpenCV简介与安装
  2. 深入Atlas系列:Web Sevices Access in Atlas示例(6) - 在客户端隐藏服务器端类型信息...
  3. 使用数字示波器DS6104测量交流信号的幅值和相位
  4. oracle表空间操作详解
  5. git commit提示Your branch is up-to-date with 'origin/master'.(做过测试不错)
  6. *2-3-7-加入field_automation机制
  7. js 求时间差 字符串转化为日期
  8. 理解 OpenStack 高可用(HA) (6): MySQL HA
  9. 服务器jbod扩展_三分钟了解服务器关键技术——RAID
  10. AI换脸APP“ZAO”刷屏并一夜爆火,它能红多久?
  11. 保留五天的日志 php,怎样让日志在归档目录保留5天?
  12. 电机控制入门——学习路线规划以及学习书籍推荐
  13. java添加文本框和标签_如何在column.expression中插入文本框或标签的值?
  14. 爬虫学习笔记1——爬取糗百段子
  15. Java后端工程师在做什么
  16. 海康威视2022届校招面经(内含内推码)
  17. HashMap 底层源码详解(jdk1.8)
  18. 莫提博客 - 简约优雅的个人博客系统
  19. jstack问题定位分析
  20. SAT作文-- 一篇优秀作文范例

热门文章

  1. nodejs安装失败
  2. 内存映射与DMA笔记
  3. weblogic linux sun/awt/X11GraphicsEnvironment
  4. NTLDR is missing Press any key to restart
  5. Java的Constructor(构造器)的理解
  6. 一场360容器圈的武林大会“360互联网技术训练营第九期—360容器技术解密与实践” (附PPT与视频)
  7. php获取cpu编码,PHP下通过exec获得计算机的唯一标识[CPU,网卡 MAC地址]
  8. mysql innodb_data_file_path_MySQL修改innodb_data_file_path参数的一些注意事项
  9. 面试必问: Spring IOC
  10. Spring中的Aop底层原理