文章目录
锁LOCK的实例
首先我们知道线程共用资源(比如下面例子的list1变量就是可以共同访问)
上一节提到,如果处理不当,线程异步的共同访问一个变量会造成数据紊乱 所以需要人为加锁,这就是Thread.Lock
看看下面的例子是怎么防止共同访问的(避免数据紊乱)
#-*- utf-8 -*-
from threading import Lock,Thread
from time import sleep,time
lock = Lock()
list1 = [0]*10
def putR(s):
lock.acquire()
for i in range(len(list1)):
list1[i] = 1
print("put ",list1[i])
sleep(s)
lock.release()
def getR(s):
lock.acquire()
for i in range(len(list1)):
print("get ",list1[i])
sleep(s)
lock.release()
if __name__ == "__main__":
t1 = Thread(target=putR, name="aa", args=(0.5,))
t2 = Thread(target=getR, name="aa", args=(0.5,))
t2.start()
t1.start()
t1.join()
t2.join()
结果
get 0
get 0
get 0
get 0
get 0
get 0
get 0
get 0
get 0
get 0
put 1
put 1
put 1
put 1
put 1
put 1
put 1
put 1
put 1
put 1
Process finished with exit code 0
这里start执行顺序将会影响线程的执行,为什么?
前面没有锁的时候我说谁先start都一样的 start顺序没太大意义
回答:这里不再是异步被调用,非阻塞的执行,而是同步调用,阻塞执行,谁先start就谁先做事(比如上一例中的get()
),另一个只能等着(比如上一例中的put()
)。类比上厕所抢位置,他先抢到,厕所上锁(acquire)“有人”,我就只能等着,等他上完,锁解除(release),我才能进去。
我们来做个实验验证一下:
如果将t1 t2 start的顺序改变
那么就会变成:
put 1
put 1
put 1
put 1
put 1
put 1
put 1
put 1
put 1
put 1
get 1
get 1
get 1
get 1
get 1
get 1
get 1
get 1
get 1
get 1
Process finished with exit code 0
run 与 start
我们简单复习一下:
start启动线程之后,调用run具体执行,执行参数包括我们传过去的target = func
参数。
如果,我们想改造线程执行的内容就更改run
(重写)。不更改start是因为start包含线程之间的很多复杂的执行内容,我们最多可以试着添加新的内容,但是不能重写。这样添加新内容还不如更改之前人家已经给好的run
呢。
死锁
这里我们自定义两个线程类ThreadA 和 ThreadB
所谓死锁,主要针对锁的嵌套问题。
线程都想获得完一个锁以后,还需要另一个锁的保护,但是只有两个锁A B。线程A获得lockA以后线程B获得lockB,这时
线程A期待lockB将会release给他
线程B期待lockA将会release给他
于是类似独木桥,
“你倒是下来啊”
“你咋不上来呢”
大致有个印象,我们来看实例:
#-*- utf-8 -*-
from threading import Thread, Lock
from time import time, sleep
lockA = Lock()
lockB = Lock()
class ThreadA(Thread):
def run(self): # 重载 start
if lockA.acquire():
print(self.name + ' get Lock A')
sleep(0.1)
if lockB.acquire():
print(self.name + ' get Lock A+B')
sleep(0.1)
lockB.release()
lockA.release()
class ThreadB(Thread):
def run(self): # 重载 start
if lockB.acquire():
print(self.name + ' get Lock B')
sleep(0.1)
if lockA.acquire():
print(self.name + ' get Lock B+A')
sleep(0.1)
lockA.release()
lockB.release()
if __name__ == '__main__':
t1 = ThreadA()
t2 = ThreadB()
t1.start()
t2.start()
t1.join()
t2.join()
ThreadA先拿到LockA是没问题的,谁先抢到厕所谁就赢了:)
执行结果:
明显,ThreadA与ThreadB互不相让,各自为政,各拿一个锁期待另一个人退一步,然后僵持在这里。
一般解决方法有两个,一个是设置timeout
,将代码的acquire属性用起来:
结果如下:
因为ThreadA先执行,都是timeout=2 于是A先释放了lockA,B就等到了,拿到了双杀 双锁
再看看另一个例子:
#-*- utf-8 -*-
from threading import Thread, Lock
from time import time, sleep
lockA = Lock()
lockB = Lock()
class ThreadA(Thread):
def run(self): # 重载 start
while True:
if lockA.acquire(timeout=2):
print(self.name + ' get Lock A')
sleep(0.1)
if lockB.acquire(timeout=2):
print(self.name + ' get Lock A+B')
sleep(0.1)
lockB.release()
lockA.release()
class ThreadB(Thread):
def run(self): # 重载 start
while True:
if lockB.acquire(timeout=2):
print(self.name + ' get Lock B')
sleep(0.1)
if lockA.acquire(timeout=2):
print(self.name + ' get Lock B+A')
sleep(0.1)
lockA.release()
lockB.release()
if __name__ == '__main__':
t1 = ThreadA()
t2 = ThreadB()
t1.start()
t2.start()
t1.join()
t2.join()
结果是这样的:
Thread-1 get Lock A
Thread-2 get Lock B
Thread-2 get Lock B
Thread-1 get Lock A
Thread-1 get Lock A
Thread-2 get Lock B
Thread-2 get Lock B
Thread-1 get Lock A
Thread-1 get Lock A
Thread-2 get Lock B
思考一下为什么会这样:)