多线程
多线程
进程和线程
进程是资源分配的最小单位,线程是CPU调度的最小单位。每一个进程中至少有一个线程。
进程
本质上就是一段程序的运行过程(抽象概念)
最小的资源单位(操作系统分配cpu,内存资源的基本单位)
线程
最小的执行单元(实例),是cpu调度和分派的基本单位
每个线程都有自己的堆栈和局部变量
线程在同一进程中的各个线程,都可以共享该进程所拥有的资源
1.一个程序至少有一个进程,一个进程至少有一个线程。(进程可以理解为线程的容器),一个进程里可以开辟多个线程和进程
2.进程在执行过程中拥有独立的内存单元,而多个线程共享这个进程的内存,从而极大地提高了程序的运行效率
3.一个线程(主线程创建子线程)可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行
同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的进程id,这意味着,线程可以访问该进程的每一个内存资源;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
并发&并行
并发:系统具有处理多个任务的能力,cpu疯狂切换(单核)
并行:系统具备同时处理多个任务的能力(多核)
同步&异步
同步:我煮饭,我等煮好饭去炒菜--等
异步:我煮饭的过程中,可以去炒菜--不等
python解释器的GIL锁
GIL解决什么问题?
Python使用引用计数来管理内存,x=1,如果线程执行到1,这个时候垃圾回收线程来执行,会发现x引用计数为0,就会把x给干掉,显然,这样是不对的。
无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行
一个python进程同一时间只有一个线程能被cpu调用(多核对它基本没有用)
但是可以开启多个进程,然后每个进程就可以各有一个线程丢给一个cpu,来实现并行
线程抢占的就是GIL锁
计算密集型:多进程效率高
I/O密集型:多线程效率高(IO过程中cpu会有空闲时间,可以利用空闲时间做别的任务)
多线程或者(多进程+携程)用于IO密集型,如socket,爬虫,web
多进程用于计算密集型,如金融分析(但是不推荐)
python中的多线程
例1
如下有三个线程,一个主线程,主线程开辟了两个子线程
主线程最先运行完毕,t1子线程运行了3秒,t2子线程运行了6秒
整个进程花费了6秒多一点结束
import threading
import time
def zx(t,s):
time.sleep(s)
print(t)
if __name__ == '__main__':
t1=threading.Thread(target=zx,kwargs={"t":"t1","s":3})
t1.start()
t1=threading.Thread(target=zx,kwargs={"t":"t2","s":6})
t1.start()
print("main")
main
t1
t2
Process finished with exit code 0
例2
和上面一样的知识点
import time
import threading
def music():
print("开始听歌")
time.sleep(3)
print("停止听歌")
def game():
print("开始玩游戏")
time.sleep(6)
print("停止玩游戏")
if __name__ == '__main__':
t1=threading.Thread(target=music)
t2=threading.Thread(target=game)
t1.start()
t2.start()
print("main")
开始听歌
开始玩游戏
main
停止听歌
停止玩游戏
Process finished with exit code 0
常用方法和属性
threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程对象。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
线程对象方法
#ident:线程的id
# run(): 线程被cpu调度后自动执行线程对象的run方法
# start():启动线程活动。
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。
join() 线程等待
join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。
看懂下面代码者,方可大成
join()相当于,执行完这个子线程,再去执行主线程下面的代码
import time
import threading
def music():
print("开始听歌")
time.sleep(3)
print("停止听歌")
def game():
print("开始玩游戏")
time.sleep(6)
print("停止玩游戏")
if __name__ == '__main__':
t1=threading.Thread(target=music)
t2=threading.Thread(target=game)
t1.start()
t1.join()
t2.start()
t2.join()
print("main")
开始听歌
停止听歌
开始玩游戏
停止玩游戏
main
Process finished with exit code 0
setDaemon()守护线程
setDaemon(True):
将线程声明为守护线程,必须在start() 方法调用之前设置,当我们 在程序运行中,执行一个主线程,主线程运行完毕,想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 setDaemon方法
例1
当主线程运行完毕,去检验t1和t2,发现他们都是守护线程,直接退出
import time
import threading
def music():
print("开始听歌")
time.sleep(3)
print("停止听歌")
def game():
print("开始玩游戏")
time.sleep(6)
print("停止玩游戏")
if __name__ == '__main__':
t1=threading.Thread(target=music)
t2=threading.Thread(target=game)
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
print("main")
开始听歌
开始玩游戏
main
Process finished with exit code 0
例2
当主线程运行完毕,去检验t1和t2,发现t1不是守护线程,t2是守护线程,t1耗时3秒,t1执行完毕,直接退出,因为t2耗时6秒,还是守护线程,主线程不用管它
import time
import threading
def music():
print("开始听歌")
time.sleep(3)
print("停止听歌")
def game():
print("开始玩游戏")
time.sleep(6)
print("停止玩游戏")
if __name__ == '__main__':
t1=threading.Thread(target=music)
t2=threading.Thread(target=game)
t2.setDaemon(True)
t1.start()
t2.start()
print("main")
开始听歌
开始玩游戏
main
停止听歌
Process finished with exit code 0
定时器
线程延迟执行
from threading import Thread,Timer
import time
def task():
print('线程执行了')
time.sleep(2)
print('线程结束了')
t = Timer(4,task) # 过了4s后开启了一个线程
t.start()
print("end")
end
线程执行了
线程结束了