文章目录
1.线程的介绍(理论部分)
1.1 进程线程分工
我们之间讲运行一个py文件,就是开启了一个进程,在内存中开辟一个进程空间,将必要的数据加载到这个进程空间中,然后CPU再去调用这个进程的主线程去执行具体的代码。一个进程里默认包含一个主线程。
进程是资源单位,线程是执行单位
a = 1
b = 2
c = a + b
if a:
print(666)
def func():
print(a - b)
return
func()
print(111)
1.2 现实举例
工厂车间的例子
1.3 进程VS线程
- 开启线程的开销低于开启进程的开销
- 开启线程的速度远高于开启进程的速度,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用。
- 线程之间的数据时共享的,这样数据就不安全,进程之间的数据是隔离的。
1.4 多线程的应用举例
现在我们知道了,如果让你并发或者并行的执行任务,两种方式:
- 开启多进程
- 一个进程下开启多线程
文件编辑器:
第一个任务监听键盘的输入
第二个任务输入到屏幕显示器。
第三个任务实时将数据保存到磁盘上。
要用多线程执行:
- 多线程数据共享,避免进程通信的队列资源浪费。
- 开启多个进程开销大,效率相对低
2. 线程创建的两种方式
2.1 函数式创建线程
方法一:
from threading import Thread
def task(n1, n2):
print(f'{n1}开始')
print(f'{n2}结束')
if __name__ == "__main__":
t1 = Thread(target=task, args=('小奶猿', '落叶'))
t1.start()
print('====主线程')
2.1.1 线程的开销小于进程
每次运行此代码,主线程永远在子线程开始之后再运行,并不是因为他们是串行,他们肯定是并行,只是因为开启线程的开销很低。
2.1.2 线程之间没有父子之分
线程之间没有主子。父子之分的,我们之所以称主线程、子线程仅仅是为了区分而已;而进程之间是有父子、主子之分,子进程的开启依赖于父进程,子进程的所有的资源都是深copy父进程而且父进程对子进程进行回收。
2.1.3 主进程为为什么要等其余线程结束后在结束。
进程是资源单位,加载各种数据资源的,但是如果进程里面的线程执行完毕,结束了,进程还有存在的必要吗?但是主进程如果也结束了,其余的线程怎么办?其余线程没有数据资源了,只能强制结束了,这样不合理、主线程代码执行完毕后,不能结束,要等待其余线程结束了,他在结束,这样进程在结束。
2.2 面向对象式创建线程
from threading import Thread
class MyThread(Thread):
def run(self):
print('子线程开始了')
if __name__ == '__main__':
t1 = MyThread()
t1.start()
print('主')
3. 进程VS线程
-
验证进程与线程的开启速度(开销)
from threading import Thread import time def task(n1, n2): print(f'{n2}回来了。。。') time.sleep(2) print(f'{n1}心花怒放。。。') if __name__ == '__main__': t1 = Thread(target=task, args=('小奶猿', '落叶')) t1.start() # 线程课开销小,速度快。 print('====线程')
-
获取线程的pid
from threading import Thread import time import os def task(): print(f'此线程的pid是{os.getpid()}') if __name__ == '__main__': t1 = Thread(target=task) t2 = Thread(target=task) t3 = Thread(target=task) t1.start() t2.start() t3.start() print('===主线程的pid是{os.getpid()}')
一个进程下的所有的线程的pid都是这同一个进程的pid。
-
同一个进程下的多线程共享数据。
from threading import Thread import time import os x = 100 def task(): global x x = 0 if __name__ == '__main__': t1 = Thread(target=task) t1.start() t1.join() print(f'====主线程{x}')
同一个进程下的多个线程一定是数据共享的。
4. 线程的其他方法
from threading import Thread
import threading
import time
import os
def task():
print('子进程。。。')
time.sleep(2)
if __name__ == '__main__':
t1 = Thread(target=task, name='线程-1') # 设置线程的名字
t2 = Thread(target=task, name='线程-2')
t3 = Thread(target=task, name='线程-3')
t1.start()
t2.start()
t3.start()
"""
# t1.start()
# print(t1.name) # 获取线程的名字
# t1.join()
# print(t1.is_alive()) # 判断线程是否存活运行
"""
print(threading.enumerate()) # 返回一个列表,存储的是当前活跃的线程对象。
print(len(threading.enumerate())) # 获取当前活跃的线程数量
print(threading.active_count()) # 获取当前活跃的线程的数量
print('====主线程')
5.守护线程
-
先回忆守护进程
from multiprocessing import Process import time def foo(): print(123) time.sleep(1) print('end123') def bar(): print(456) time.sleep(3) print("end456") if __name__ == '__main__': p1 = Process(target=foo) p2 = Process(target=bar) p1.daemon = True p1.start() p2.start() print('main-------')
总结:
- 守护进程是等待主进程代码结束即结束。
-
守护线程
from threading import Thread import time def task(n1, n2): print(f'{n1}心花怒放。。。') time.sleep(2) print(f'{n2}回来了') if __name__ == '__main__': t1 = Thread(target=task, args=('小奶猿', '落叶')) t1.daemon = True t1.start() print('====主线程')
-
再次测试
from threading import Thread import time def foo(): print(123) time.sleep(1) print('end123') def bar(): print(456) time.sleep(3) print('end456') if __name__ == '__main__': t1 = Thread(target=foo) t2 = Thread(target=bar) t1.daemon = True t1.start() t2.start() print('main-----')
守护线程要等待主线程的程序结束之后,在结束,主线程程序什么时候结束?主线程程序要等待其余的子线程结束之后再结束。
守护线程要等待主线程以及其余的非守护线程的子线程全部结束之后,再结束。
from threading import Thread import time def foo(): print(123) time.sleep(3) print('end123') def bar(): print(456) time.sleep(1) print('end456') if __name__ == '__main__': t1 = Thread(target=foo) t2 = Thread(target=bar) t1.daemon = Ture t1.start() t2.start() print('main------')
-