并发编程

抛砖引玉,同大家一起交流;可能会从两个大方向上来开始:一是来看看为什么并发编程难,包括涉及的一些基础硬件和操作系统相关的一点知识;二是一起来分析一下常同的并发模型,我们怎么通一个简单高效的并发模型来写出简单的并发实现(如线程与锁/actor/csp/);

并发与并行

并发:是逻辑上发生的同时;如一个处理器同时处理多个任务,CPU时间片发生切换

并行:是物理上发生的同时;是多核处理器同时处理多个任务;

系统硬件体系架构

这里做一个假设,如果CPU1在对一个变量执行一个CAS的操作,而该变量的缓存行是在CPU7的高速缓存里面,那么可能的执行顺序可能是:

CPU1检查本地高速缓存,没有找到缓存行;

请求转发到CPU0与CPU1的互联模块,检查CPU0的本地高速缓存,没有找到缓存行;

请求转发到系统互联模块,检查其它三个芯片,得知缓存行被CPU6与CPU7所在的芯片持有;

请求被转发到CPU6和CPU7的互联模块,检查这两个CPU的高速缓存,在CPU7的高速缓存中找到缓存行;

CPU6将缓存行发送给所属的互联模块,并刷新自己高速缓存中的缓存行;

CPU6和CPU7的互联模块将缓存行发送给系统互联模块;

系统互联模块将缓存行发送给CPU0和CPU1的互联模块;

CPU0和CPU1的互联模块将缓存行发送给CPU1的高速缓存;

CPU1对高速缓存中的变量执行CAS操作;

这是一个简化并忽略了某些复杂的事件序列,因为:

其它CPU可能试图在相同的缓存行上执行并发的CAS操作;

缓存行可能被只读复制到其它的CPU高速缓存中,这种情况下有必要刷新它们的缓存;

当请求到达时,CPU7可能已经在缓存上操作,这种情况下CPU7必须你保留这个请求,直到请求完成、

CPU7可能已经从缓存中排出它的缓存行,这样当请求到达时,缓存行已经写入内存中了;

在缓存行中可能发生一个可纠正的错误,因此需要在使用数据前纠正它;

...

CPU的缓存一致性极其复杂,所以高效率(榨干最后一滴CPU资源,每瓦特性能)且可靠的并行编程总是太不容易!

除了CPU高速缓存还有许多其它因素,如内存引用、原子操作、内存屏障、I/O操作等等。

内存引用

微处理器从内存里读一个值的时间,微处理器可以用这段时间执行成成百甚至上行知指令,虽然一直在极大的减少内存访问的延迟,但是仍只有高度可以预测的数据访问模式才能让缓存发挥最大效果;

原子操作

CPU会通过一条『流水线』来控制CPU内部的指令流(现在微处理器都可以支持多条流水线并行),这种架构使得CPU流水线的可以一次执行多个操作,而原子操作正与这种特性有冲突;比如一种常见的技巧是标出所有包含原子操作所需数据的流水线,保证CPU在操作时,这些流水线都属于正在执行原子操作的CPU;如果我们按CPU一个时钟周期执行一条指指令(约为0.6ns),一个最好情况下的CAS操作也需要40个时钟周期;

内存屏障

这个比较好理解,一般有Load Barrier 和 Store Barrier即读屏障和写屏障,如JAVA中的volatile关键字

寄存器在执行前,为了提高性能,会对指令重排,而内存屏障会禁止指令重排;

强制将CPU高速存中的数据写回内存,让缓存中相关的数据失效,这又涉及到内存总线 CPU与内存的读写等等的性能损耗;

JVM对内存屏障的详细内容可以参考一下<>,作者是阿里大神方腾飞,也是 并发编程网 博主,总之很牛逼。

I/O操作

如高速缓存未命中(CPU之间的I/O),如果涉及到网络,大容量存储(磁盘),这类操作对于性能的影响更是远远大于上面提到几种的开销;

所以并行编程变得复杂,除了与硬件的交互、还有任务分割、并行访问控制等;这些如线程、锁、屏障等,我们在实际工作中应尽量避免直接控制它们,因为它们都有怪脾气,处处是陷井;庆辛随着为并发设计编程语言的兴起(Elang,Scala,Golang等),在释放多核威力的同时,也一定程度降低了并发程序的难度。特别Golang也是Docker这类明星产品的实现语言。

Golang并发模式(CSP)并发内核

(注,该图引自网络)

其中M是一个内核线程,P是调度器,G是一个协程,灰色的G为挂起的协程;Go通过协程goroutine提供语言层面的调度器,实现高效的M:N(M个用户线程对应N个os内核线程)对应关系,使用goroutine做为并发实体,非常的轻量级,理论可以很轻松的创建上十万个goroutine。

P作为调度器,作用类似于CPU的核,每个工作线程都必须绑定一个有效的P才被允许执行,否则只能休眠等到有空闲的P时被唤醒;P还要为线程提供执行资源,如为对象分配内存 本地任务队更等;

而实际的执行体是M(OS内核线程),和P绑定 不停的获取执行G的并发任务。M通过修改寄存器,将执行栈指向G自带的栈内存;

P/M是执行的组合体,但是两者数量并非是一一对应,M由调度器按需创建,比如当M陷于一个IO操作长时间阻塞,P就会被监控线程抢回去去创建或唤醒一个M执行其它任务。

如果想深入了解,建议可以看看雨痕写的 <<源码解析>> 郁闷的点是有汇编和C的代码...

从一段简单的代码来看

主协程调用producer,创建了个生产者协程,并返回了一个通道,这里也可以理解为一个服务。

主协程的do_something执行业务逻辑。

JAVA的同学从简单理解就当chan就是JAVA中的队列(用通讯的方式共享内存)

先做抛砖引玉,后续再专门针对Go 和Actor做一些分享,交流。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容