Python3与C#并发编程之 线程篇-锁专题

2.2.加强篇

1.多次获取导致死锁

2.常见的死锁

Lock案例

优化下

2.2.1.线程同步~互斥锁Lock

2.2.2.线程同步~递归锁RLock

2.2.3.死锁引入

2.2.4.线程同步~条件变量Condition

2.2.5.线程同步~信号量Semaphore(互斥锁的高级版)

2.2.加强篇

其实以前的 是没有线程这个概念的, 程序员经常使用线程,这一看~方便啊,然后可能是当时程序员偷懒了,就把进程模块改了改(这就是为什么之前说Linux下的多进程编程其实没有Win下那么“重量级”),弄了个精简版进程==> (内核是分不出 的,反正 个数都是一样)

多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享(全局变量和堆 ==> 线程间共享。进程的栈 ==> 线程平分而独占

还记得通过 获取的线程信息吗?难道线程也没个id啥的?一起看看:(通过

回顾:进程共享的内容:(回顾:http://www.cnblogs.com/dotnetcrazy/p/9363810.html)

代码(.text)

文件描述符(fd)

内存映射(mmap)

2.2.1.线程同步~互斥锁Lock

线程之间共享数据的确方便,但是也容易出现数据混乱的现象,来看个例子:

输出:(应该是 ,发生了数据混乱,只剩下 )

Lock案例

这时候 就该上场了

互斥锁是实现线程同步最简单的一种方式,读写都加锁(读写都会串行)

先看看上面例子怎么解决调:

输出:

优化下

lock设置为全局或者局部,性能几乎一样。循环换成map后性能有所提升(测试案例在Code中)

输出:

本来多线程访问共享资源的时候可以并行,加锁后就部分串行了(没获取到的线程就阻塞等了)

项目中可以多次加锁,每次加锁只对修改部分加(尽量少的代码)】(以后会说协程和Actor模型

补充:以前都是这么写的,现在支持 托管了(有时候还会用到,所以了解下):【net是直接 】

扩展知识:(GIL在扩展篇会详说)

GIL的作用:多线程情况下必须存在资源的竞争,GIL是为了保证在解释器级别的线程唯一使用共享资源(cpu)。

同步锁的作用:为了保证解释器级别下的自己编写的程序唯一使用共享资源产生了同步锁

2.2.2.线程同步~递归锁RLock

看个场景:小明欠小张2000,欠小周5000,现在需要同时转账给他们:(规定:几次转账加几次锁

小明啥也没管,直接撸起袖子就写Code了:(错误Code示意

小明写完代码就出去了,这可把小周和小张等急了,打了N个电话来催,小明心想啥情况?

一看代码楞住了,改了改代码,轻轻松松把钱转出去了:

输出:

就这么算了吗?不不不,不符合小明性格,于是小明研究了下,发现~还有个递归锁 呢,正好解决他的问题:

RLock内部维护着一个 变量, 的次数,从而使得资源可以被多次 。直到一个线程所有的 ,其他的线程才能获得资源

2.2.3.死锁引入

1.多次获取导致死锁

小明想到了之前说的(互斥锁 读写都加锁)就把代码拆分研究了下:

输出发现:(第二次加锁的时候,变成阻塞等了【死锁】)

这种方式,Python提供的RLock就可以解决了

2.常见的死锁

看个场景:小明和小张需要流水帐,经常互刷~

一般来说,有几个共享资源就加几把锁(小张、小明就是两个共享资源,所以需要两把 )

先描述下然后再看代码:

正常流程小明给小张转1000:小明自己先加个锁==>小明-1000==>获取小张的锁==>小张+1000==>转账完毕

死锁情况小明给小张转1000:小明自己先加个锁==>小明-1000==>准备获取小张的锁。可是这时候小张准备转账给小明,已经把自己的锁获取了,在等小明的锁(两个人相互等,于是就一直死锁了)

代码模拟一下过程:

输出:(卡在这边了)

项目中像这类的情况,一般都是这几种解决方法:(还有其他解决方案,后面会继续说)

按指定顺序去访问共享资源

在访问其他锁的时候,先把自己锁解了

trylock的重试机制

得不到全部锁就先放弃已经获取的资源

比如上面的情况,我们如果规定,不管是谁先转账,先从小明开始,然后再小张,那么就没问题了。或者谁钱多就谁(权重高的优先)

输出:

2.2.4.线程同步~条件变量Condition

条件变量一般都不是锁,只能能阻塞线程,从而减少不必要的竞争,Python内置了 (不指定就是RLock)

看看源码:

再看看可不可以进行with托管:(支持)

看个生产消费者的简单例子:(生产完就通知消费者)

输出:(list之类的虽然可以不加global标示,但是为了后期维护方便,建议加上)

通知方法:

notify() :发出资源可用的信号,唤醒任意一条因 wait()阻塞的进程

notifyAll() :发出资源可用信号,唤醒所有因wait()阻塞的进程

2.2.5.线程同步~信号量Semaphore(互斥锁的高级版)

记得当时在分析 源码的时候,有提到过(点我回顾)

同进程的一样, 管理一个内置的计数器,每当调用 时内置函数 ,每当调用 时内置函数

通俗讲就是:在互斥锁的基础上封装了下,实现一定程度的并行

举个例子,以前使用互斥锁的时候:(厕所就一个坑位,必须等里面的人出来才能让另一个人上厕所)

使用信号量之后:厕所坑位增加到5个(自己指定),这样可以5个人一起上厕所了==>实现了一定程度的并发

举个例子:(Python在语法这点特别爽,不用你记太多异同,功能差不多基本上代码也就差不多)

输出:

可能看了上节回顾的会疑惑:源码里面明明是 ,搞啥呢?

其实 就比 多了个在调用 时检查计数器的值是否超过了计数器的初始值,如果超过了将抛出一个异常

以上一个案例说事:你换成 和上面效果一样==>

下级预告:银行家算法,哲学家吃面经典算法,Event,Queue,Actor...

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180825G0ILCB00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券