(8)什么是线程(如何开启线程)以及线程的其他属性
为什么会出现线程
进程的缺点:
1、非常消耗资源,计算机不能无限开启子进程
2、如果开了过多的进程,cpu的切换进程的模式下是非常耗时的
因为进程的缺点,线程的出现就是为了解决进程的缺点,线程的开销小于进程
1、所以线程就是轻量级的进程
2、一个进程里面至少有一个线程
3、线程就是具体干活的,执行任务的
PS:进程相当于一座工厂,线程相当于干活的人
PS:进程是一个资源的实体单位,而cpu操作的最小单位是线程
理论案例:
QQ是一个主进程
QQ内有几个功能
1、聊天
2、支付
3、视频
PS:如果没有线程,就要开3个进程,这样计算机的消耗就会变大
PS:线程的启动速度要高于进程的速度
开启QQ主进程后,下面所有的功能就交给线程去执行,这样既对计算机的资源消耗低了,而且速度也快了
线程的好处
1、线程的开启速度快于进程
2、一个进程下的线程和线程之间是共享进程的资源
3、cpu在线程之间的切换速度远快于进程
PS:同一个进程下的数据在线程之间是共享的
线程和进程使用的场景
线程的使用场景:有大量IO存在的时候,使用线程 # IO就是一个读写的过程,包括网络请求
进程的使用场景:有密集计算的时候使用进程
开启线程的方式有两种
1、函数的方式
import time
from threading import Thread # 从threading 包导入Thread模块,Thread模块就是开启子线程的
def task(arg):
print("%s is running..."% arg)
time.sleep(1) #这个模拟IO
print("%s is done..."% arg)
if __name__ == '__main__':
t = Thread(target=task,args=('子线程,')) #这是开启子线程,子线程肯定是在主线程里面开启
t.start() #子线程在开启的瞬间就执行了
print('主线程') #这里为什么是主线程,因为运行整段程序的时候是需要开一个进程,进程里面就有线程
PS:子线程的开启代码写法和主线程基本一样
PS:子线程要快于主线程
PS:开启子线程后,下面运行的肯定是主线程,而开启子进程后面运行的肯定是主进程
2、类的方式
from threading import Thread
class MyThread(Thread): #继承包中的开启子进程模块
def run(self): #重新定义模块下的run函数
print("hell0")
if __name__ == '__main__':
t = MyThread()
t.start()
print('主线程')
线程和进程速度对比实例
import time
from multiprocessing import Process
from threading import Thread
def task(arg):
print("%s is running..."%arg)
time.sleep(1) #这个模拟IO
print("%s is done..." %arg)
if __name__ == '__main__':
t = Thread(target=task,args=('子线程',))
p = Process(target=task,args=('子进程',))
t.start()
p.start()
print('主线程')
PS:子线程先开启,然后主线程,子进程要等很久才开启,由于子进程开启需要向操作系统申请内存空间,在申请资源,所以进程的速度慢于线程
同一个进程下开启线程共享数据
from threading import Thread
x = 100
def task():
global x # x定义成全局
x = 0
if __name__ == '__main__':
t = Thread(target=task)
t.start() #开启一个线程
print(x)
PS:可以看到线程下的程序运行时候把x的值已经修改了,说明线程下的数据是共享的,而在进程下是无法修改的,所以进程的数据是不共享的
线程的其他属性
1、t.is_alive() # 查看线程是否存活
from threading import Thread
def task():
print('xxxxxxxxx')
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print(t.is_alive()) #查看线程是否存活,存活就是True,死了就是False
print('主')
2、t.join() # 等待子线程执行完毕
例1:
from threading import Thread
def task():
print('xxxxxxxxx')
if __name__ == '__main__':
t = Thread(target=task)
t.start()
t.join() #等待子线程执行完毕
print(t.is_alive())
print('主')
PS:由于子线程开启是异步的,所以如果用join等待子线程执行完毕再查看线程是否存活,肯定是False
例:2
import time
from threading import Thread
def task():
print('xxxxxxxxx')
time.sleep(1) #这个模拟IO
if __name__ == '__main__':
start = time.time()
t_l = []
for i in range(10):
t = Thread(target=task)
t_l.append(t) # 将线程放入列表
t.start()
for t in t_l:
t.join() #等待每个线程结束
end = time.time()
print(end-start)
PS:利用for循环一次开启10个线程,然后用join方法等待线程执行完毕,计算所有线程执行的时间,由于线程之间是异步的,所以同时开了10个线程,10个线程同时睡了1秒,所以结果开启线程到线程结束只用了一秒
current_thread类下方法使用(.name查看当前线程名)
from threading import Thread,current_thread #必须从threading包下倒入current_thread类
def task():
print('xxxxxxxxx')
print(current_thread().name) #这里就是用类current_thread().name查看当前线程名
if __name__ == '__main__':
t = Thread(target=task,name='QQ')
t.start()
print('主')
active_count方法查看当前活跃的线程数
from threading import Thread,active_count #从threading包下倒入active_count方法
def task():
print('xxxxxxxxx')
if __name__ == '__main__':
t = Thread(target=task,name='QQ')
t.start()
print(active_count()) #查看当前活跃的线程数
print('主')
守护线程
守护进程:守护的是父进程
守护线程:守护的是进程内的其他线程(比如一个进程内有10个线程,有一个是守护线程,这个守护线程要等另外的9个线程执行完毕才会死),守护的是整个进程的运行周期
开启守护线程
import time
from threading import Thread,current_thread #current_thread定义守护线程的名字
def task():
print('%s is running...'%current_thread().name) # 打印守护线程的名字
time.sleep(1) #这个模拟IO
print('%s is done...' % current_thread().name)
if __name__ == '__main__':
t = Thread(target=task,name='守护进程')
t.daemon=True #daemon就是开启守护线程
t.start()
print('主线程')
守护线程实例
from threading import Thread
import time
def foo():
print(123)
time.sleep(3) #这个就是模拟IO
print('end123')
def bar():
print(456)
time.sleep(1) #这个就是模拟IO
print('end456')
t1 = Thread(target=foo)
t2 = Thread(target=bar)
t1.daemon=True
t1.start()
t2.start()
print('main---------')
PS:开启了两个线程t1和t2,t1设置了一个守护线程,t1必须等着t2执行完以及主线程执行完才会死亡,t1.start和t2.start开启线程时候会立马执行函数,t1执行foo函数然后会睡3秒,这时候同时t2执行bat函数然后睡1秒后执行完毕,主线程执行完毕,所以t1子线程里面最后一个不会执行,因为主线程已经执行完毕,守护进程直接死亡,t1这个子线程结束