一、守护线程
守护线程的使用方法和守护进程类似,也是deamon=True的方式来启动一个守护线程,但是具体的执行结果和守护进程略有不同。
from threading import Thread
from time import sleep
def func1():
while True:
print("$$$$$")
sleep(2)
def func2():
sleep(6)
print("子线程运行结束")
t1 = Thread(target=func1)
t1.daemon = True
t1.start()
t2 = Thread(target=func2)
t2.start()
print("主线程")
----------------
$$$$$
主线程
$$$$$
$$$$$
子线程运行结束
这个例子当中,t1为守护线程,如果按照守护进程的定义,子线程会随着主线程的运行结束而结束,那么t1也应该随着主线程的运行结束而结束,但实际运行结果是t1随着t2的结束才结束。
那这是为什么呢?
实际上主进程会随着主线程的结束,回收进程的资源。
- 主进程结束时会把守护进程进行回收,因此守护进程会随着主进程的结束而结束,再等待不是守护进程的进程结束进行回收,不然会产生僵尸进程。
- 而主线程必须等待所有的线程结束之后才算结束,不然会导致所有资源被回收,因此守护线程会在主线程代码结束之后继续运行。
二、线程锁
线程之间用锁的情况比进程之间会多很多,因为进程之前数据的安全性比线程高很多。
而线程之间用锁,我们会遇到一个死锁的问题,经典的一个问题就吃面问题。
我们假设三个人A、B、C围着一张桌子吃面,要吃到面,必须要同时拿到叉子和面,那为了保证不会有三个人同时吃面的问题,只有一把叉子和一碗面,那么就如同三个线程和两把锁,只有同时拿到两把锁,才可以进行代码的运行。
那会遇到这么一种情况,A拿到了叉子,B抢到了面,但是由于谁都不满足吃面的条件,他们也不把叉子和面换回去,因此就僵持住了。就像两把锁都没有释放,这就是死锁的问题。
递归锁
而解决这种问题的这种方式就是递归锁,递归锁的机制就是你必须先拿到锁A,才能拿锁B,之后拿锁C,以此递进。而释放锁的时候,是反过来的一个过程,我们用一个代码的例子来看看,就是上面的吃面的问题。
from threading import Thread,RLock
food_lock = fork_lock = RLock() #递归锁
def eat(name):
food_lock.acquire()
print("拿到食物了")
fork_lock.acquire()
print("拿到叉子了")
print("%s开吃"%name)
food_lock.release()
fork_lock.release()
def eat2(name):
food_lock.acquire()
print("拿到食物了")
fork_lock.acquire()
print("拿到叉子了")
print("%s开吃"%name)
food_lock.release()
fork_lock.release()
t1 = Thread(target=eat,args=("lufei",))
t2 = Thread(target=eat2,args=("zuoluo",))
t3 = Thread(target=eat,args=("naridou",))
t4 = Thread(target=eat2,args=("sasigai",))
t1.start()
t2.start()
t3.start()
t4.start()