python中async/await的理解和使用
进程、线程、协程
进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
通俗来说,如下图所示,打开win11的任务管理器,里面的Apifox就是一个进程,Google Chrome谷歌浏览器也是一个进程,微信等等这就是一个进程
线程
线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
通俗来说,如下图所示,打开win11的任务管理器,Pycharm展示的7条就是7个线程,也就是说,一个进程里面有多个线程,这个7个线程不是同步进行的,而是异步,这样的话,就会有上下文切换,浪费时间
守护线程
thread_list = []
for info_dict in video_list:
save_path = BASE_DIR + "/image/" + str(info_dict['id']) + "/"
sing_thread = threading.Thread(target=main, args=(save_path, info_dict['carmera_url'],info_dict['name'],))
thread_list.append(sing_thread)
for t in thread_list:
# 设置为守护线程,不会因主线程结束而中断
t.setDaemon(True)
# 开启线程
t.start()
# 设置完成任务后线程结束
for t in thread_list:
t.join()
协程
进程和线程是计算机提供的,协程是程序员创造的,不存在于计算机中。这样的话,由程序员控制,就会很方便灵活,
协程(Co-routine),也可称为微线程,或非抢占式的多任务子例程,一种用户态的上下文切换技术(通过一个线程实现代码块间的相互切换执行)
意义:在一个线程(协程)中,遇到io等待时间,线程可以利用这个等待时间去做其他事情
async/await的使用
同步和异步协程的比较
假设有三个任务,里面都有大量的i/o等待时间
同步
import datetime
import time
def test1():
time.sleep(3)
print('washing1 finished')
def test2():
time.sleep(2)
print('washing2 finshed')
def test3():
time.sleep(5)
print('washing3 finshed')
if __name__ == '__main__':
time1 = datetime.datetime.now()
test1()
test2()
test3()
time2 = datetime.datetime.now()
# 0:00:10.032949
print(time2-time1)
协程
import asyncio
import datetime
async def test1():
await asyncio.sleep(3) # 使用 asyncio.sleep(), 它返回的是一个可等待的对象
print('washer1 finished')
async def test2():
await asyncio.sleep(2)
print('washer2 finished')
async def test3():
await asyncio.sleep(5)
print('washer3 finished')
if __name__ == '__main__':
"""
事件循环机制分为以下几步骤:
1. 创建一个事件循环
2. 将异步函数加入事件队列
3. 执行事件队列, 直到最晚的一个事件被处理完毕后结束
4. 最后建议用 close() 方法关闭事件循环, 以彻底清理 loop 对象防止误用
"""
time1 = datetime.datetime.now()
# 1. 创建一个事件循环
loop = asyncio.get_event_loop()
# 2. 将异步函数加入事件队列
tasks = [
test1(),
test2(),
test3(),
]
# 3. 执行事件队列, 直到最晚的一个事件被处理完毕后结束
loop.run_until_complete(asyncio.wait(tasks))
# 4. 如果不再使用 loop, 则关闭
loop.close()
time2 = datetime.datetime.now()
# 0:00:05.009762
print(time2-time1)
由执行时间可以看到,同步使用了10秒,而协程使用的5秒,可以看出协程的精髓在于,遇到等待即切换,而且没有上下文的损耗,更快,但是注意的是,协程是运行到一个线程里面的,那么,它的缺点就是,无法充分利用多核CPU的特点
python3.6和3.7的区别
python3.6
python3.6需要自己手动创建事件循环
事件循环:去检索一个任务列表的所有任务,并执行所有未执行任务,直至所有任务执行完成
# python 源码
import asyncio
async def func1():
print('协程1')
async def func2():
print('协程2')
# task可为列表,即任务列表
# task = func1()
task = [func1(), func2()]
# 创建事件循环
loop = asyncio.get_event_loop()
# 添加任务,直至所有任务执行完成
loop.run_until_complete(asyncio.wait(task))
#关闭事件循环
loop.close()
# 事件循环关闭后,再次调用loop,将不会再次执行。
python3.7
python3.7不需要我们手动创建事件循环,它会自动帮我们创建
相当于同步,只能说明基础语法
import asyncio, datetime
async def func1(name):
print(name, "启动")
await asyncio.sleep(2)
print(name, "关闭")
async def main():
await func1("动作1")
await func1("动作2")
await func1("动作3")
if __name__ == '__main__':
time1 = datetime.datetime.now()
asyncio.run(main())
time2 = datetime.datetime.now()
# 0:00:06.018858
print(time2-time1)
"""
动作1 启动
动作1 关闭
动作2 启动
动作2 关闭
动作3 启动
动作3 关闭
"""
改进版
import asyncio
import datetime
async def func1(name):
print(name, "启动")
await asyncio.sleep(2)
print(name, "关闭")
async def main():
await asyncio.gather(
asyncio.create_task(func1("动作1")),
asyncio.create_task(func1("动作2")),
asyncio.create_task(func1("动作3")),
)
if __name__ == '__main__':
time1 = datetime.datetime.now()
asyncio.run(main())
time2 = datetime.datetime.now()
# 0:00:02.006539
print(time2-time1)
"""
动作1 启动
动作2 启动
动作3 启动
动作1 关闭
动作2 关闭
动作3 关闭
"""
通过上面两个例子打印的时间以及打印字符串的顺序,我们可以看出create_task的作用,虽然还是同步,但是是遇到堵塞就里面启动第二个
异步函数调用异步函数
import asyncio
import datetime
async def func2(name):
await asyncio.sleep(2)
print(name, "我是func2")
async def func1(name):
print(name, "启动")
await func2(name)
print(name, "关闭")
async def main():
await asyncio.gather(
asyncio.create_task(func1("动作1")),
asyncio.create_task(func1("动作2")),
asyncio.create_task(func1("动作3")),
)
if __name__ == '__main__':
time1 = datetime.datetime.now()
asyncio.run(main())
time2 = datetime.datetime.now()
# 0:00:02.006539
print(time2-time1)
"""
动作1 启动
动作2 启动
动作3 启动
动作1 我是func2
动作1 关闭
动作2 我是func2
动作2 关闭
动作3 我是func2
动作3 关闭
"""
由上面的例子可以看出,协程里面调用协程,其步骤是同步的
进程、线程的本质
首先,我们知道了进程是系统进行资源和分配的基本单位,而线程是操作系统能够进行运算调度的最小单位,所以要明白,多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的
进程、线程、协程的对比:
(1)协程既不是进程也不是线程,协程仅仅是一个特殊的函数,协程与进程和进程不是一个维度的。
(2)一个进程可以包含多个线程,一个线程可以包含多个协程。
(3)一个线程内的多个协程虽然可以切换,但是多个协程是串行执行的,只能在一个线程内运行,没法利用CPU多核能力。
(4)协程与进程、线程一样,切换是存在上下文切换问题的
更多推荐
所有评论(0)