python 多线程 数据库死锁_第十五章、Python多线程同步锁,死锁和递归锁

第十五章、Python多线程同步锁,死锁和递归锁

1. 引子:

1.创建线程对象

t1 = threading.Thread(target=say,args=('tony',))

2.启动线程

t1.start()

后面又说了两个点就是join和守护线程的概念

​以上就是python多线程的基本使用

​说明:前面说的两个功能是相互独立的,相互不干涉的,不会用到同享的资源或者数据,如果我们多个线程要用到相同的数据,那么就会存在资源争用和锁的问题,不管在什么语言中,这个都是不能避免的。 那么接下来讲讲同步锁,死锁和递归锁的使用

2.同步锁

​锁通常被用来实现对共享资源的同步访问。为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁。

​适用同步锁的例子如下:

import threading

import time

num = 100

def fun_sub():

global num

# num -= 1

num2 = num

time.sleep(0.001)

num = num2-1

if __name__ == '__main__':

print('开始测试同步锁 at %s' % time.ctime())

thread_list = []

for thread in range(100):

t = threading.Thread(target=fun_sub)

t.start()

thread_list.append(t)

for t in thread_list:

t.join()

print('num is %d' % num)

print('结束测试同步锁 at %s' % time.ctime())

-----------------------------------------------------

开始测试同步锁 at Sun Apr 28 09:56:45 2019

num is 91

结束测试同步锁 at Sun Apr 28 09:56:45 2019

这样的例子描述:创建100的线程,然后每个线程去从公共资源num变量去执行减1操作,按照正常情况下面,等到代码执行结束,打印num变量,应该得到的是0,因为100个线程都去执行了一次减1的操作。

这样的问题是:发现结果不是0而是91

让我们整理一下代码思路:

1.因为GIL,只有一个线程(假设线程1)拿到了num这个资源,然后把变量赋值给num2,sleep 0.001秒,这时候num=100

2.当第一个线程sleep 0.001秒这个期间,这个线程会做yield操作,就是把cpu切换给别的线程执行(假设线程2拿到个GIL,获得cpu使用权),线程2也和线程1一样也拿到num,返回赋值给num2,然后sleep,这时候,其实num还是=100.

3.线程2 sleep时候,又要yield操作,假设线程3拿到num,执行上面的操作,其实num有可能还是100

4.等到后面cpu重新切换给线程1,线程2,线程3上执行的时候,他们执行减1操作后,其实等到的num其实都是99,而不是顺序递减的。

5.其他剩余的线程操作如上

解决方案:这里就要借助于python的同步锁了,也就是同一时间只能放一个线程来操作num变量,减1之后,后面的线程操作来操作num变量。看看下面我们怎么实现。

import threading

import time

num = 100

def fun_sub():

global num

lock.acquire()

print('----加锁----')

print('现在操作共享资源的线程名字是:',t.name)

num2 = num

time.sleep(0.001)

num = num2-1

lock.release()

print('----释放锁----')

if __name__ == '__main__':

print('开始测试同步锁 at %s' % time.ctime())

lock = threading.Lock() #创建一把同步锁

thread_list = []

for thread in range(100):

t = threading.Thread(target=fun_sub)

t.start()

thread_list.append(t)

for t in thread_list:

t.join()

print('num is %d' % num)

print('结束测试同步锁 at %s' % time.ctime())

------------------------------------------------

.......

----加锁----

现在操作共享资源的线程名字是: Thread-98

----释放锁----

----加锁----

现在操作共享资源的线程名字是: Thread-100

----释放锁----

num is 0

结束测试同步锁 at Sun Apr 28 12:08:27 2019

思路:看到上面我们给中间的减1代码块,加个一把同步锁,这样,我们就可以得到我们想要的结果了,这就是同步锁的作用,一次只有一个线程操作同享资源。

3.死锁

引子:

​死锁的这个概念在很多地方都存在,比较在数据中,大概介绍下死锁是怎么产生的

# 线程1拿到了(锁头2)想要往下执行需要(锁头1),

# 线程2拿到了(锁头1)想要往下执行需要(锁头2)

# 互相都拿到了彼此想要往下执行的必需条件,互相都不放手里的锁头.

# 产生了死锁问题

产生原因:python中在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。

from threading import Thread,Lock

mutex1 = Lock()

mutex2 = Lock()

import time

class MyThreada(Thread):

def run(self):

self.task1()

self.task2()

def task1(self):

mutex1.acquire()

print(f'{self.name} 抢到了 锁1 ')

mutex2.acquire()

print(f'{self.name} 抢到了 锁2 ')

mutex2.release()

print(f'{self.name} 释放了 锁2 ')

mutex1.release()

print(f'{self.name} 释放了 锁1 ')

def task2(self):

mutex2.acquire()

print(f'{self.name} 抢到了 锁2 ')

time.sleep(1)

mutex1.acquire()

print(f'{self.name} 抢到了 锁1 ')

mutex1.release()

print(f'{self.name} 释放了 锁1 ')

mutex2.release()

print(f'{self.name} 释放了 锁2 ')

for i in range(3):

t = MyThreada()

t.start()

那么,为了解决这个死锁问题,就引入了递归锁方案

4.递归锁RLock

原理:

​为了支持在同一线程中多次请求同一资源,python提供了"递归锁":threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源

不多说,放代码

from threading import Thread,Lock,RLock

# mutex1 = Lock()

# mutex2 = Lock()

mutex1 = RLock()

mutex2 = mutex1

import time

class MyThreada(Thread):

def run(self):

self.task1()

self.task2()

def task1(self):

mutex1.acquire()

print(f'{self.name} 抢到了 锁1 ')

mutex2.acquire()

print(f'{self.name} 抢到了 锁2 ')

mutex2.release()

print(f'{self.name} 释放了 锁2 ')

mutex1.release()

print(f'{self.name} 释放了 锁1 ')

def task2(self):

mutex2.acquire()

print(f'{self.name} 抢到了 锁2 ')

time.sleep(1)

mutex1.acquire()

print(f'{self.name} 抢到了 锁1 ')

mutex1.release()

print(f'{self.name} 释放了 锁1 ')

mutex2.release()

print(f'{self.name} 释放了 锁2 ')

for i in range(3):

t = MyThreada()

t.start()

总结:

​上面我们用一把递归锁,就解决了多个同步锁导致的死锁问题。大家可以把RLock理解为大锁中还有小锁,只有等到内部所有的小锁,都没有了,其他的线程才能进入这个公共资源。

5. 大总结

​还有一点,并不是所有的多线程都存在数据不同步、死锁的问题,但在访问共享资源的时候,锁是一定要存在了, 所以我们在代码里面加锁的时候,要注意在什么地方加,对性能的影响最小,这个就靠对逻辑的理解了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值