主要来看下守护线程的打印结果和守护进程的区别
文章目录
1 守护线程
1.1 没有非守护线程的情况:
from threading import Thread
import time
def test1():
print(123)
time.sleep(1)
print('end 123')
if __name__ == '__main__':
t1 = Thread(target=test1)
t1.daemon = True # 设置为守护线程
t1.start()
print('主线程')
# 123 (线程t1瞬间开启,打印123,之后sleep1秒足够主线程代码运行结束,线程t1被回收,不会打印end123)
# 主线程
1.2有非守护线程的存在:
from threading import Thread
import time
def test1():
print(123)
time.sleep(1)
print('end 123')
def test2():
print(456)
time.sleep(3)
print('end 456')
if __name__ == '__main__':
t1 = Thread(target=test1)
t1.daemon = True
t2 = Thread(target=test2)
t1.start()
t2.start()
print('主线程')
# 123
# 456
# 主线程
# end 123
# end 456
线程t1,t2几乎瞬间开启之后sleep,主线程运行到打印之后,此时,主线程并没有结束,主线程要在其他非守护线程都运行完毕后才算运行完毕,所以要等到非守护线程t2运行结束之后才能回收进程整体的资源,t2 sleep 3秒,足够t1运行完毕。
1.3 与守护进程相比:
from multiprocessing import Process
import time
def test1():
print(123)
time.sleep(1)
print('end 123')
def test2():
print(456)
time.sleep(3)
print('end 456')
if __name__ == '__main__':
p1 = Process(target=test1)
p1.daemon = True
p2 = Process(target=test2)
p1.start()
p2.start()
print('主进程')
# 主进程
# 456
# end 456
主进程在其代码结束后就已经算运行完毕了,守护进程此时就被回收,主进程会等非守胡进程运行完毕后回收子进程的资源,才算运行结束。
2 互斥锁
from threading import Thread
def test():
global n
temp = n # 全局变量n
time.sleep(2)
n = temp - 1
if __name__ == '__main__':
n = 100
t_list = []
for i in range(100): # 开启100个子线程
t = Thread(target=test)
t_list.append(t)
t.start()
for t in t_list: # 等100个子线程都结束
t.join()
print('此时n:', n)
# 此时n: 99
开启了100个子线程,子线程共用数据,每个子线程都减去1,为什么不是0呢?因为100个子线程在2秒内,几乎瞬间开启,拿到的n 此时都是100,减去1之后,此时n是99
需要加锁,等一个线程处理完之后释放锁,另一个线程才能抢锁z继续处理,不过效率是比较低的。
from threading import Thread
from threading import Lock
def test():
global n
lock.acquire() # 加锁
temp = n # 全局变量n
time.sleep(2)
n = temp - 1
lock.release() # 释放锁
if __name__ == '__main__':
lock = Lock()
n = 100
t_list = []
for i in range(100): # 开启100个子线程
t = Thread(target=test)
t_list.append(t)
t.start()
for t in t_list: # 等100个子线程都结束
t.join()
print('此时n:', n)
# 此时n: 0
3死锁
死锁是两个或两个以上的线程或进程在执行过程中,因争夺资源造成的互相等待的现象,若无外力作用,他们都将无法进行下去,此时成系统处于死锁状态。
from threading import Thread, Lock
import time
lock_A = Lock()
lock_B = Lock()
class MyTest(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
lock_A.acquire()
print('%s拿到A锁' % self.name)
lock_B.acquire()
print('%s拿到B锁' % self.name)
lock_B.release()
print('%s释放B锁' % self.name)
lock_A.release()
print('%s释放A锁' % self.name)
def func2(self):
lock_B.acquire()
print('%s拿到B锁' % self.name)
time.sleep(2)
lock_A.acquire()
print('%s拿到A锁' % self.name)
lock_A.release()
print('%s释放A锁' % self.name)
lock_B.release()
print('%s释放B锁' % self.name)
if __name__ == '__main__':
for i in range(10):
t = MyTest()
t.start()
# Thread-1拿到A锁
# Thread-1拿到B锁
# Thread-1释放B锁
# Thread-1释放A锁
# Thread-1拿到B锁
# Thread-2拿到A锁 卡住了
线程1开启之后
func1 抢到A锁之后,其他线程处于等待A锁状态,于是线程1又拿到B锁,然后释放B锁,然后释放A锁,其他线程开始抢A锁,线程1执行func2,拿到B锁,然后sleep 2秒,此时所有线程都已开启。线程2执行func1时,拿到A锁之后,取拿B锁。而 线程1 sleep之后取拿A锁,线程1和线程2僵持在这,处于死锁状态。
4 递归锁 RLock
递归锁和自定义的互斥锁的区别是,互斥锁只能acquire一次,其他线程必须等锁 release之后才能再去拿到锁。
而递归锁可以acquire多次,在递归锁内部增加了一个计数,记录了acquire此数,acqui一次就+1,release一次就-1,直到一个线程所有的acquire都release之后,其他线程才能获得资源。
还是上面的代码,用递归锁,就不会产生死锁状态。
from threading import Thread, RLock
import time
lock_A = lock_B = RLock() # 递归锁
class MyTest(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
lock_A.acquire()
print('%s拿到A锁' % self.name)
lock_B.acquire()
print('%s拿到B锁' % self.name)
lock_B.release()
print('%s释放B锁' % self.name)
lock_A.release()
print('%s释放A锁' % self.name)
def func2(self):
lock_B.acquire()
print('%s拿到B锁' % self.name)
time.sleep(2)
lock_A.acquire()
print('%s拿到A锁' % self.name)
lock_A.release()
print('%s释放A锁' % self.name)
lock_B.release()
print('%s释放B锁' % self.name)
if __name__ == '__main__':
for i in range(10):
t = MyTest()
t.start()
线程1执行func1,拿到A锁之后counter+1,拿到B锁之后counter+1,释放B锁,counter-1,释放A锁counter-1,此时counter是0,此时所有开启的线程都可抢递归锁,
线程1拿到B锁之后counter+1,其他线程处于等待状态,sleep 2秒,所有线程已开启,依然在等待,线程1sleep之后自然拿到A锁,counter+1,释放A锁,counter-1,释放B锁counter-1,所有线程都可抢递归锁。
执行结果每个线程执行func1 和func2时并不是连续的,执行完func1此时计数器是0,所有线程都可以抢锁。
5 信号量 Semaphore
信号量也是一把锁,可以指定信号量n,只不过,互斥锁同一时间只有一个任务抢到锁执行,信号量同一时间允许n个任务执行。
from threading import Thread, Semaphore, currentThread
import time, random
import time
sm = Semaphore(3)
def test():
with sm:
print('%s in' % currentThread().getName())
time.sleep(random.randint(1, 3)) # 模拟每个任务执行时间不一样
if __name__ == '__main__':
for i in range(10):
t = Thread(target=test)
t.start()
Thread-1 in
Thread-2 in
Thread-3 in
Thread-4 in
Thread-5 in
Thread-6 in
Thread-7 in
Thread-8 in
Thread-9 in
Thread-10 in
6 Event
每个线程都是独立运行的,当一个线程的运行需要根据其他线程运行状态确定下一步操作时需要用到Event,Event是threading 库中的Event对象,是一个信号标志,初始情况为False
event.isSet():返回event的状态值
event.wait():等待event
event.set():设置event状态为True,所有等待event的线程都会被激活
event.clear():恢复默认值
from threading import Thread, Event
event = Event()
def student():
print('正在听课')
event.wait() # 等待.wait可以设置时间,如果event,wait(1)等待1秒后自动激活
print('下课')
def teacher():
print('上课')
time.sleep(3)
print('出去玩')
event.set() # 激活
if __name__ == '__main__':
s1 = Thread(target=student)
t1 = Thread(target=teacher)
t1.start()
s1.start()
7 定时器 Timer
指定时间之后执行操作
from threading import Timer
def test():
print('hello')
t = Timer(1, test) # 两个参数,1代表时间间隔秒,test代表要执行的功能,如果需要参数可以args=传参,t就是一个线程。
t.start()