1. 线程join
回忆:进程join是让主进程阻塞等待,等待子进程执行完毕后,主进程再次执行。线程join同理
from threading import Thread, currentThread
import time
def task():
print(f'{currentThread().getName()}开始了')
time.sleep(2)
print(f'{currentThread().getName()}结束了')
if __name__ == '__main__':
t1 = Thread(target=task, name='线程1')
t1.start()
t1.join()
print('=====主进程')
2. 线程锁
2.1 线程操作公共数据问题
from threading import Thread, currentThread
import time
x = 100
def task():
global x
print(1111)
temp = x
print(333)
temp -= 1
print(22222)
x = temp
if __name__ == '__main__':
t_list = []
for i in range(100):
t = Thread(target=task,)
t.start()
t_list.append(t)
for i in t_list:
i.join()
print(f'======主线程{x}')
2.2 加锁处理
from threading import Thread, currentThread, Lock
import time
x = 100
def task(lock):
global x
print(1111)
lock.acquire()
temp = x
print(22222)
lock.release()
x = temp
if __name__ == '__main__':
lock = Lock()
t_list = []
for i in range(100):
t = Thread(target=task, args=(lock,))
t.start()
t_list.append(t)
for i in t_list:
i.join()
print(f'====主线程{x}')
小结:
- 无论是进程还是线程加锁就是为了让锁住的程序变成串行处理,保证数据安全。
3. 死锁线程
from threading import Thread, currentThread, Lock
import time
A_lock = Lock()
B_lock = Lock()
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
A_lock.acquire()
print(f'{self.name}拿到A锁')
B_lock.acquire()
print(f'{self.name}拿到B锁')
B_lock.release()
print(f'{self.name}释放了B锁')
A_lock.release()
print(f'{self.name}释放了A锁')
def func2(self):
B_lock.acquire()
print(f'{self.name}拿到了B锁')
time.sleep(1)
A_lock.acquire()
print(f'{self.name}拿到了A锁')
A_lock.release()
print(f'{self.name}释放了A锁')
B_lock.release()
print(f'{self.name}释放了B锁')
if __name__ == '__main__':
for i in range(3):
t = MyThread()
t.start()
print('====主')
小结:
- 虽然锁可以保证数据的安全,但是锁多了之后,容易造成死锁现象。进程锁同理
- 如果以后项目中真的需求两把以上的锁才能解决问题,那么我们就不用这种同步锁(互斥锁)了,我要用递归锁。
4. 递归锁
-
引子
递归锁就是一把锁,这一把锁可以所多次,也可以释放多次,递归锁上有引用计数功能,锁一次+1一次,解锁一次,-1一次,递归锁不允许其他的线程或者进程抢夺知道递归锁上的引用计数为0时。
-
测试
from threading import Thread, RLock import time A_lock = RLock() B_lock = A_lock class MyThread(Thread): def run(self): self.func1() self.func2() def func1(self): A_lock.acquire() print(f'{self.name}拿到了A锁') B_lock.acquire() print(f'{self.name}拿到了B锁') B_lock.release() print(f'{self.name}释放了B锁') A_lock.release() print(f'{self.name}释放了A锁') def func2(self): B_lock.acquire() print(f'{self.name}拿到了B锁') time.sleep(1) A_lock.acquire() print(f'{self.name}拿到了A锁') A_lock.release() print(f'{self.name}释放了A锁') B_lock.release() print(f'{self.name}释放了B锁') if __name__ == '__main__': for i in range(3): t = MyThread() t.start() print('====主')
-
进程递归锁:
5. 信号量
5.1 引子
信号量其实也是锁,但是这个锁比较特殊,之前我们讲的互斥锁就是同一时间只允许一个进程(线程)持有这把锁,知道释放掉,其他进程才可以竞争这把锁。而信号量同时可以允许N个进程(线程)持有这把锁,一个或者几个释放掉,其他的一个或者几个才可以进入。
举一个形象但是不文明的例子:信号量就是公共厕所。5个坑位,同事一个时刻只能5个人进入,怎么保证5个人?门口放5把钥匙,拿一个进一个月。当5把钥匙都被取走之后,剩下的人想进入只能等着,等有人释放完毕,将钥匙放到门口,其他的人再去争抢这把钥匙,这就是信号量。
from threading import Thread, currentThread, Semaphore
import time
import random
def go_wc(sem):
sem.acquire()
print(f'{currentThread().getName()}成功抢到坑位')
time.sleep(random.randint(1, 3))
print(f'{currentThread().getName()}成功结束')
if __name__ == '__main__':
sem = Semaphore(5)
for i in range(20):
t = Thread(target=go_wc, args=(sem,))
t.start()