同步和异步
同步(Synchronous): 多个任务之间执行的时候要求有先后顺序,必须一个先执行完成之后,另一个才能继续执行, 只有一个主线。如:你说完,我再说(同一时间只能做一件事情)
异步(Asynchronous):多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多条运行主线。如:发微信(可以不用等对方回复,继续发)、点外卖(点了外卖后,可以继续忙其他的事情,而不是坐等外卖,啥也不做)
多任务介绍
多任务的最大好处是充分利用CPU资源,提高程序的执行效率。
1. 多任务的概念
多任务是指在同一时间内执行多个任务,例如: 现在电脑安装的操作系统都是多任务操作系统,可以同时运行着多个软件。
2. 多任务的执行方式
多任务分为并发和并行两种方式
2.1. 并发
在一段时间内交替去执行任务。
例如:
对于单核cpu处理多任务,操作系统轮流让各个软件交替执行,假如:软件1执行0.01秒,切换到软件2,软件2执行0.01秒,再切换到软件3,执行0.01秒……这样反复执行下去。表面上看,每个软件都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像这些软件都在同时执行一样,这里需要注意单核cpu是并发的执行多任务的。
2.2. 并行
对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的软件,多个内核是真正的一起执行软件。这里需要注意多核cpu是并行的执行多任务,始终有多个软件一起执行。
小结
- 使用多任务就能充分利用CPU资源,提高程序的执行效率,让你的程序具备处理多个任务的能力。
- 多任务执行方式有两种方式:并发和并行,这里并行才是多个任务真正意义的同时在执行。
多任务开发-线程
1. 线程概述
1.1. 线程介绍
在Python中,想要实现多任务可以使用线程来完成,线程是实现多任务的一种方式。还可以使用进程、协程实现多任务。
1.2. 线程概念
进程是分配资源的基本单位, 一旦创建一个进程就会分配一定的资源,
线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程。
1.3. 线程作用
多线程可以完成多任务
多线程效果图:
2. 线程编程
2.1. Python默认单任务
接下来我们使用python代码来模拟“唱歌跳舞”这件事情
import time
def sing():
"""唱歌函数"""
for i in range(3):
print("正在唱歌..")
time.sleep(0.5)
def dance():
"""跳舞函数"""
for i in range(3):
print("正在跳舞..")
time.sleep(0.5)
if __name__ == '__main__':
sing() # 正在唱歌
dance() # 正在跳舞
运行结果如下:
- 很显然刚刚的程序并没有完成唱歌和跳舞同时进行的要求
2.2. 多线程完成多任务
import threading
import time
# 唱歌任务
def sing():
# 扩展: 获取当前线程
# print("sing当前执行的线程为:", threading.current_thread())
for i in range(3):
print("正在唱歌...%d" % i)
time.sleep(1)
# 跳舞任务
def dance():
# 扩展: 获取当前线程
# print("dance当前执行的线程为:", threading.current_thread())
for i in range(3):
print("正在跳舞...%d" % i)
time.sleep(1)
if __name__ == '__main__':
# 扩展: 获取当前线程
# print("当前执行的线程为:", threading.current_thread())
# 创建唱歌的线程
# target: 线程执行的函数名
sing_thread = threading.Thread(target=sing)
# 创建跳舞的线程
dance_thread = threading.Thread(target=dance)
# 开启线程
sing_thread.start()
dance_thread.start()
执行结果:
正在唱歌...0
正在跳舞...0
正在唱歌...1
正在跳舞...1
正在唱歌...2
正在跳舞...2
2.3. 小结
- 导入线程模块
import threading
- 创建子线程并指定执行的任务
sub_thread = threading.Thread(target=任务名)
- 启动线程执行任务
sub_thread.start()
给线程添加参数
1. 带有参数的任务线程
前面我们使用线程执行的任务是没有参数的,假如我们使用线程执行的任务带有参数,如何给函数传参呢?
Thread类执行任务并给任务传参数有两种方式:
args
表示以元组的方式给执行任务传参kwargs
表示以字典方式给执行任务传参
2. args元组参数
示例代码:
import threading
import time
# 带有参数的任务
def task(count):
for i in range(count):
print("任务执行中..")
time.sleep(0.2)
else:
print("任务执行完成")
if __name__ == '__main__':
# 创建子线程
# args: 以元组的方式给任务传入参数
sub_thread = threading.Thread(target=task, args=(5,))
sub_thread.start()
执行结果:
任务执行中..
任务执行中..
任务执行中..
任务执行中..
任务执行中..
任务执行完成
3. kwargs字典参数
示例代码:
import threading
import time
# 带有参数的任务
def task(count):
for i in range(count):
print("任务执行中..")
time.sleep(0.2)
else:
print("任务执行完成")
if __name__ == '__main__':
# 创建子线程
# kwargs: 表示以字典方式传入参数
sub_thread = threading.Thread(target=task, kwargs={"count": 3})
sub_thread.start()
执行结果:
任务执行中..
任务执行中..
任务执行中..
任务执行完成
4. 小结
- 线程执行任务并传参有两种方式:
- 元组方式传参(args) :元组方式传参一定要和参数的顺序保持一致。
- 字典方式传参(kwargs):字典方式传参字典中的key一定要和参数名保持一致。
守护线程
1. 守护线程概念
守护线程:如果在程序中将子线程设置为守护线程,则该子线程会在主线程结束时自动退出,设置方式为,
thread.daemon = True
注意:要在thread.start()
之前设置,默认是False
的,也就是主线程结束时,子线程依然在执行。
对于python应用我们都知道main方法是入口,它的运行代表着主线程开始工作了,我们也知道Python虚拟机里面有垃圾回收器的存在使得我们放心让main飞奔,然而这背后的故事是垃圾回收线程作为守护着主线程的守护线程默默的付出着。
旧时代皇帝驾崩
妃子陪葬
如下代码,主线程执行了exit()
【其实并没有真正结束】,子线程还在继续执行
import threading
import time
def work1():
for i in range(10):
print("正在执行...", i)
time.sleep(0.5)
if __name__ == '__main__':
# 创建子线程
t1 = threading.Thread(target=work1)
# 启动子线程
t1.start()
# 睡2秒
time.sleep(2)
print("Game Over")
# 让程序退出
# 当主线程睡眠2秒,开始结束字节的时候,但是子线程还没有结束,默认情况下,子线程继续执行
exit()
2. 设置守护线程
#...
# 设置子线程 t1 为守护线程(如果主线程结束了,也随之结束)
t1.daemon = True
# 启动子线程
t1.start()
#...