并发思想提炼(1)(理解并发,避免死锁)

并发思想提炼(1)(理解并发,避免死锁)html

一直作服务器后端和基础组件平台开发,经常用到并发,故简单放些干货,一来算是总结,二来但愿后人少走弯路, 写到哪儿算哪儿,不按期更新。python

1.    Introduction编程

先来明白一些概念。Concurrency并发和Multi-thread多线程不一样后端

你在吃饭的时候,忽然来了电话。安全

  1. 你吃完饭再打电话,这既不并发也很少线程
  2. 你吃一口饭,再打电话说一句话,而后再吃饭,再说一句话,这是并发,但很少线程。
  3. 你有2个嘴巴。一个嘴巴吃饭,一个嘴巴打电话。这就是多线程,也是并发。

并发:表示多个任务同时执行。可是有可能在内核是串行执行的。任务被分红了多个时间片,不断切换上下文执行。服务器

多线程:表示确实有多个处理内核,可同时处理多个任务。多线程

世界的复杂的,世界是并发的。因而模拟这世上的业务的程序也是并发的。随着系统功能的增长,复杂度不断提升,并发特性被引入编程。架构

2.    最简单的并发并发

两个任务互不干扰,它们不会影响到系统的同一实体。每一个线程只须要本身作好本身的任务便可。线程

两个任务会影响到同一实体,可是不会在同时访问该同一实体。这样,在这个实体上,任务是串行执行的。这样也是安全的并发。

3.    危险的并发

两个任务同时访问同一实体,脏读写的问题,设有a值为1, 两个线程前后加1,按道理说最后a值结果为3.

  1. T1线程读取数据a;T2线程读取数据a;
  2. T1线程a++, 而后反存到a,a值为2;
  3. T2线程a++, 而后反存到a, a值仍是为2;

两次操做后,a值不为3,而是2。这就是并发出现的错误。a如果一个可释放(disposable)的实体. T1线程释放a,T2线程操做a,会形成更大的错误。

4.    如何避免此危险

1. 干脆就串行执行T1,T2。不过没有利用处处理器的并发特性。虽然安全,可是效率不高。

2. T1,T2并发。可是不会同时操做同一个实体(Critical Entity)。即实体不是并发的。实体是串行的。

3. 读取关键实体使用Clone,拷贝出来的实体是临时的。本次操做在该临时实体上。下次操做继续Clone该实体再使用。

常常能用到的是方法2和方法3。接下来具体说说方法2和3。

5.    让对关键实体的访问串行

方法2的核心思想是串行。不过不是任务串行,而是访问共享实体时串行。串行是人类容易思惟的方式,把并发问题转变为顺序执行问题,也助于后来维护人员理解。一个最简单的实现方式就是加Lock then access。

 

6.    Lock地狱

Lock是并发程序中经常使用的操做,每一个人都会用。。。。嗯。。。。经常会滥用。。。而后,运行一段时间。嘣,程序自爆。说说经常遇到的问题:

  1. 死锁。T1 lock A request B,T2 lock B request A。T1和T2被互相Block住。程序进行不了
  2. 递归锁。T1 lock A and lock A again。同一线程T1被本身Block住。

递归锁好解决,C++ 11中有std::recursive_mutex。再高级一点的语言自带这样的特性。好比C#中的lock就自带递归锁。通常地,递归锁经过里面添加counter实现,每次锁就counter++,每次release就counter--。Counter 为0就表示解锁。其实这就是一种信号量(semaphore)的实现方法。引伸开来去,C++中的share_ptr/auto_ptr就是此类思想,这种经过引用计数来判断该对象是否被使用,若检测到不被使用从而自动的release该段内存。C#和Java中内存管理就是这个思路。很少讲了,之后单开段子讲。

7.    死锁及其防止

确切的说死锁单纯在程序这个层面难以防止,最好在设计开始就注意这个问题。就是说在程序设计的时候就搞明白那些资源会互相调用。这样的状况就要多留心眼。我工做中一直写一些基础架构API。当其它工程师调用时。。。。。嗯。。。。。不仔细看文档,在一些不适用的地方使用形成了死锁,而后来找我。。。。。这种状况下要么就多培训,多强调使用手册。要么,这样说吧,把API写得健壮点。毕竟彻底不会知道用户会怎么使用。这就是我在API开发中使用静态语言的缘由(其实我更喜欢动态语言python,用python作leetcode真是心情舒畅),至少在编译阶段就能有必定程度的规则控制,而不是到了运行阶段报Error。这关系到如何编写健壮的API,之后开段子讲。

回到死锁防止这个问题,关于避免死锁的API可使用这个方法---try lock机制。简单就是说给lock一个时间锁,若是在一段时间内仍是没有取得该资源的访问权就跳过,放LOG,而后执行下面的步骤。能够经过前面讲的“等待信号量”来实现此机制,其实也不用本身特别实现,各主流语言应该都有。核心思想在软件层面上是放个自旋锁和Flag量,以为搞不明白(C++,C#,Java等该功能实现方式)的话本身也能够实现一个。

 

第2篇 Lock free, 轮询和线程池

相关文章
相关标签/搜索