线程同步
共享数据:
- 如果多个线程共同对某个数据修改,可能出现不可预料的结果,为了保证数据的安全性,需要对多个线程进行同步,一个一个的完成,一个做完另一个才能进来。
- 效率会降低。
多线程的优势在于可以同时运行多个任务,但是当线程需要共享数据时,可能存在数据不同步的问题。
为了避免这个问题,引入了锁
的概念。
python对线程加锁主要有Lock
和Rlock
模块
使用Tread对象
的Lock
和Rlock
可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。
Lock模块
用例
import threading
from time import sleep
lock = threading.Lock()
list1 = [0] * 10
def task1():
# 获取线程锁,如果已经上锁,则等待锁的释放。
lock.acquire() # 阻塞
for i in range(len(list1)):
list1[i] = 1
sleep(0.5)
lock.release()
def task2():
# 获取线程锁,如果已经上锁,则等待锁的释放。
lock.acquire() # 阻塞
for i in range(len(list1)):
print("----->",list1[i])
sleep(0.5)
lock.release()
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t2.start()
t1.start()
t2.join()
t1.join()
print(list1)
输出:
-----> 0
-----> 0
-----> 0
-----> 0
-----> 0
-----> 0
-----> 0
-----> 0
-----> 0
-----> 0
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Lock有acquire()和release()方法,这两个方法必须是成对出现的,acquire()后面必须release()后才能再acquire(),否则会造成死锁
死锁
开发过程中使用线程,在线程间共享多个资源的时候,如果两个线程占有一部分资源并且同时等待对方的资源,就会造成死锁
。
尽管死锁很少发生,但是一旦放生就会造成应用停止响应,程序不做任何事情。
避免死锁:
- 重构代码
- 使用timeout参数
from threading import Thread,Lock
from time import sleep
lockA = Lock()
lockB = Lock()
class Mythread1(Thread):
def run(self) -> None: # 执行start()方法自动寻找run()方法
if lockA.acquire(): # 如果可以获取到锁,返回True
print(self.name+'获取到A锁!')
sleep(0.1)
if lockB.acquire(timeout=1): # 阻塞,现在等待不到B锁,设定时间自动释放锁
print(self.name+'又获取到了B锁!原来还有A锁!')
lockB.release()
lockA.release()
class Mythread2(Thread):
def run(self) -> None: # 执行start()方法自动寻找run()方法
if lockB.acquire(): # 如果可以获取到锁,返回True
print(self.name+'获取到B锁!')
sleep(0.1)
if lockA.acquire():
print(self.name+'又获取到了A锁!原来还有B锁!')
lockA.release()
lockB.release()
if __name__ == '__main__':
t1 = Mythread1()
t2 = Mythread2()
t1.start()
t2.start()
t1.join()
t2.join()
print('结束!!!!!!!!!!!!!')
输出:
Thread-1获取到A锁!
Thread-2获取到B锁!
# 此处Thread-1等待B锁因为timeout=1等待1秒后,自动释放了
Thread-2又获取到了A锁!原来还有B锁!
结束!!!!!!!!!!!!!