线程
上回我们说到GIL锁了,这不仅是python弊病,也是解释型语言的弊病。我相信在不久后,python的官方团队或者各位读者会把这个问题解决了的。那我们就接着往下学习线程。
1.进程与线程的效率问题
我们举个例子,我觉得例子能够很好的解释其中的道理。
import time
from threading import Thread
from multiprocessing import Process
def func(n):
n + 1
if __name__ == '__main__':
start = time.time()
t_lst = []
for i in range(100):
t = Thread(target=func,args=(i,))
t.start()
t_lst.append(t)
for t in t_lst:t.join()
t1 = time.time() - start
start = time.time()
t_lst = []
for i in range(100):
t = Process(target=func, args=(i,))
t.start()
t_lst.append(t)
for t in t_lst: t.join()
t2 = time.time() - start
print(t1,t2)
输出的结果是:
多线程:0.012920379638671875 多进程:4.705415725708008
结果相差 391.66倍
2.线程的其他方法
使用这些方法需要引用
import threading
# print(threading.active_count()) # 查看存活线程数
#print(threading.current_thread()) 获取当前线程对象
# print(threading.enumerate()) 获取所有线程对象
threading.get_ident()) # 获取当前线程的id
还有一些方法是 实例方法
# isAlive(): 返回线程是否活动的
# getName(): 返回线程名
# setName(): 设置线程名
3.守护线程 和 主线程 其他子线程的关系
守护线程听着很熟悉,和之前学习的守护进程差不多。但又不一样,不一样在哪里呢? 这里举一段例子进行说明
def func1():
while True:
time.sleep(1)
print('这是守护线程')
def func2():
time.sleep(8)
print('这是其他子线程')
if __name__ == '__main__':
t1 = Thread(target=func1)
Thread(target=func2).start()
t1.daemon = True
t1.start()
print('这是主线程')
time.sleep(3)
输出结果为:
解释:
在这个例子的解释就是 主线程和守护线程和子线程共同执行3秒后,这时候主线程的代码执行完毕 但是看到子线程没执行完,所以它也不会结束。守护线程看着主进程没有结束,它也会执行 直到子线程执行完毕
总结:主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。
大家看到线程的关系之后,不经联想起之前学过的进程的关系。那我在这里总结一下进程的 主进程 守护进程 和其他子进程的关系
def func1():
while True:
time.sleep(0.5)
print('这是守护进程')
def func2():
print('这是其他子进程')
time.sleep(10)
if __name__ == '__main__':
p = Process(target=func1)
p.daemon = True
p.start()
Process(target=func2).start()
print('这是主进程')
time.sleep(3)
结果:
解释:主进程和守护进程以及子进程会共同执行3秒,然后主进程代码执行完毕。这时候,主进程停下来 等待子进程结束。守护进程看主进程结束了,就停止了
总结:主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束。