8. 告别Locknode
不是一直说Lock比较麻烦危险吗,那就不要好了。其实有一个Lock free的方法。数据库
首先引入一个概念——原子变量。在这种变量上的操做是原子操做(atomic operation)。原子操做就是说这个操做要么都完成,要么都不完成,部分完成是不行的。就像物理化学中的原子同样,借用不可再分的意思。按照这样理解,对这个原子变量的访问操做就一定是串行的。一个原子操做完成后才能进行另外一个原子操做。这样子的变量类型多半是基本变量,什么int啊double啊boolean啊之类。编程
就能够简单这样想,只要调用原子操做,就能保证对象的串行访问。设计模式
经常使用语言原子操做内部实现基本用到了锁。原子操做经常使用的有++,--,compare & swap。特别地,这个CAS(compare and swap)配合循环操做就能实现lock free方法。看下面的stack.push(…)伪代码。架构
1 Atomic<Node*> head; 2 Node* new_node= new Node(….); 3 New_node->next=head; (1) 4 While(!head.compare_and_swap(new_node->next, new_node)); (2)
设有一个Stack,须要push一个值进去,head指针申明为原子量。(1)把新值的next设为head。(2)更新head,若是head==new_node->next那么,head=new_node,返回true,跳出循环;不然new_node->next更新为head,并返回false,继续循环。猜测?什么状况会new_node->next不等于head?若是在(1)执行完毕后head被其它线程修改。此时的old_head(new_node->next)就须要更新为当前最新的head,才能进行接下来的操做。这种方式实现的stack push就是用了lock free思想。是否是比加lock代码看起来简洁多了? 代码中一个lock操做都没有看到哦。并发
Lock free须要一个自旋锁,耗CPU时间,而lock方法是直接block住,释放占用的CPU时间。发现没?这个“自旋锁”思想在前文的try lock中也出现了?甚至能够这样猜测,若是把全部的锁都换成自旋锁,是否是就能防止死锁了?前面的stack.push操做也能够添加CAS试作次数,操做必定次数或时间限制,表示这次push失败,也就跳出了此自旋锁。另外,没有所谓的lock free就是比lock好,根据不一样的业务状况,自行选择吧。分布式
问:若是个人关键实体是一个对象,那么能够把对象最为一个原子量吗?这个,恐怕不能,不过能够分解对象中的一些基本类型字段做为原子量。到底使用哪些基本类型,须要对业务需求有深入的理解。函数式编程
9. 轮询操做函数
观察者设计模式这里很少说了,主要思想是把主动轮询,转变为被动通知。不过在某些并发程序集中“轮询”比“通知”思想还要广泛,或者说“经过轮询来通知”。甚至这样说,我感受在某些场合轮询比通知更有用,特别是对顺序要求敏感的地方。这类程序基于规避乱序风险从而选择此架构方式,并且轮询的架构方式能人为制造“程序栅栏”。Thread barrier这个操做据说过吧,这是一种同步程序的方式。这种方式在某些分布式程序中用到(特别是消息队列模块),并且很是利于Debug。性能
一个完整的系统轮询和通知都很重要,没有谁好谁很差一说。写到这里想一想,在上文轮询中,提到“顺序”两个字,你懂的,顺序执行容易引起性能瓶颈,及其连锁反应。并且轮询数据库什么的,数据多起来能直接把系统搞得不响应好吗,考虑下触发器嘛。因此架构就是一种舍一种得的心态,要根据具体业务需求来定夺。
“通知”的理解之后有机会再说。对了,轮询时记得yield, 别到时候轮询线程一跑起来把CPU时间片占光了,其它并发操做受到影响。
10. 线程操做
线程是执行一个函数,颇有函数式编程理念的感受。可是并非线程开得越多程序处理就越快,这和实际处理器数和线程上下文切换频率就很大关系,这些状况baidu google一下就知道,不细说,基本上最好的线程数量就大概是空闲处理器的数量。
这里说的一个思想是线程池Thread pool。就是说线程建立后不会由于执行完毕而被销毁,它会放入一个池子中等待新任务唤醒它,而后开始执行。线程建立是要耗CPU资源的,重复利用固然是好的。这个功能在高级语言中有对应的实现,好比说C#的task,它的默认调度类就实现了此思想。因此说高级语言使用起来比较顺畅,你不用从头开始造轮子。C++的boost库中也有相似方法。编程的时候用上呗,何乐而不为?
如上图,2个线程作5个任务,而不是用5个线程。并且在task3以后,线程1就处于空转或阻塞的等待状态而不是销毁自身,直到task4出现又开始执行。可是,若是5个task并行执行,且确实有如此多空闲处理器,开5条线程,处理效率更高。线程池的方式是否适用此场景还需好好考虑下。
咱们看得稍微抽象点,前文所说的线程池技术,就是任务调度操做,这个之后有机会在说吧。。。
to be continue...