线程同步(线程锁)
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)但是当线程需要共享数据时,可能存在数据不同步的问题。为了避免这种情况,引入了锁的概念。
1 lock = threading.Lock() 2 lock.acquire() # 请求得到锁 3 lock.release() # 释放锁 4 只要不释放其他的线程都无法进入运行状态
1、初始程序:
import threading
import random
import time
lock = threading.Lock()
list1 = [0] * 10 # [0,0,0,0,0,0,0,0,0,0]
def task1():
for i in range(len(list1)):
list1[i] = 1 # random.randint(1, 100)
time.sleep(0.5)
print(list1)
def task2():
for i in range(len(list1)):
print(f'当前的值是{i}')
time.sleep(0.5)
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
运行结果:
当前的值是0
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
当前的值是1
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
当前的值是2
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0]
当前的值是3
[1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
当前的值是4
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
当前的值是5
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
当前的值是6
[1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
当前的值是7
[1, 1, 1, 1, 1, 1, 1, 1, 0, 0]
当前的值是8
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0]
当前的值是9
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
进程已结束,退出代码0
2、程序加锁:
1 import threading 2 import random 3 import time 4 5 lock = threading.Lock() 6 7 list1 = [0] * 10 # [0,0,0,0,0,0,0,0,0,0] 8 9 10 def task1(): 11 lock.acquire() # 请求得到锁 12 for i in range(len(list1)): 13 list1[i] = 1 # random.randint(1, 100) 14 time.sleep(0.5) 15 print(list1) 16 lock.release() # 释放锁 17 18 19 def task2(): 20 # 获取线程锁,如果已经上锁,则等待锁的释放 21 lock.acquire() 22 for i in range(len(list1)): 23 print(f'当前的值是{i}') 24 time.sleep(0.5) 25 lock.release() 26 27 28 if __name__ == '__main__': 29 t1 = threading.Thread(target=task1) 30 t2 = threading.Thread(target=task2) 31 32 t1.start() 33 t2.start()
运行结果:
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
当前的值是0
当前的值是1
当前的值是2
当前的值是3
当前的值是4
当前的值是5
当前的值是6
当前的值是7
当前的值是8
当前的值是9
进程已结束,退出代码0
死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。尽管死锁很少发生,但一旦发生就会造成应用的停止响应,程序不做任何事情。
为什么会产生死锁?
1.因为系统资源不足。
2.进程运行推进的顺序不合适。
3.资源分配不当
例如:
死锁是因为多线程访问共享资源,由于访问的顺序不当所造成的,通常是一个线程锁定了一个资源A,而又想去锁定资源B;在另一个线程中,锁定了资源B,而又想去锁定资源A以完成自身的操作,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在等待,而无法执行的情况。
死锁案例:
1 from threading import Thread, Lock 2 import time 3 4 lockA = Lock() 5 lockB = Lock() 6 7 8 class MyThread1(Thread): # 自定义线程 9 def run(self): 10 if lockA.acquire(): 11 print(f"{self.name}获取了A锁....") 12 time.sleep(0.1) 13 14 if lockB.acquire(timeout=5): # 超时自动释放锁,自动调用 release() 15 print(f"{self.name}又获取了B锁,原程序中还有A锁") 16 lockB.release() 17 18 lockA.release() 19 20 21 class MyThread2(Thread): 22 def run(self): 23 if lockB.acquire(): 24 print(f"{self.name}获取了B锁....") 25 time.sleep(0.1) 26 27 if lockA.acquire(timeout=5): # 超时自动释放锁,自动调用 release() 28 print(f"{self.name}又获取了A锁,原程序中还有B锁") 29 lockA.release() 30 31 lockB.release() 32 33 34 if __name__ == '__main__': 35 t1 = MyThread1() 36 t2 = MyThread2() 37 38 t1.start() 39 t2.start()
原运行结果:
解决死锁后的运行结果:
Thread-1获取了A锁.... Thread-2获取了B锁.... Thread-1又获取了B锁,原程序中还有A锁 进程已结束,退出代码0
产生死锁的条件有四个:
1.互斥条件:所谓互斥就是进程在某一时间内独占资源。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
只要破坏了一个必要条件,那么我们的死锁就解决了