什么是协程?
作用:协程(微线程)可以在单进程或单线程模式下,大幅度提升程序运行效率!!
为什么使用协程?
异步处理多个网页操作
举个例子:假设我们有从多个网页爬取数据的要求,我们设定多个线程进行异步去执行获取网页的操作,当其中一个网页阻塞时,可以不必一直等待,会先去执行其它操作。
可是!计算机中启动的进程/线程那么多,你确定每次CPU进行进程/线程切换,都会切换到网页下载的进程/线程中吗?答案是不一定,因为这个进程/线程切换是由操作系统实现的,无法人为干涉。那么,这些网页下载任务的执行的效率就降低下来了。因此,可以使用协程来解决该问题!
协程处理多个网页内容下载任务
具体来说,当使用协程时,程序员可以手动控制任务的切换和调度,而不是依赖于操作系统的线程或进程调度器。在协程中,任务的切换是通过挂起(暂停)当前任务,并将控制权交给下一个任务来实现的。这种任务切换是在用户空间中进行的,不需要向操作系统发出系统调用。
因此使用协程后可以实现让计算机尽可能多的分配CPU给我们,这样也就达到了提升程序执行效率的目的。
协程的优点:
- 轻量级:协程是轻量级的,占用的系统资源少,创建和销毁的开销小。相比于线程和进程,协程的切换更加高效。
- 可控性:协程的调度和切换是由程序员自己控制的,不需要依赖操作系统进行调度,这使得编程模型更加灵活。可以根据实际需求自定义任务的调度逻辑,实现更加精细的任务切换。
asyncio模块
特殊函数:
在函数定义前添加一个async关键字,则该函数就变为了一个特殊的函数!
async def get_requests(url):
#代码
return #返回值
- 特殊函数的特殊之处是什么?
- 1.特殊函数被调用后,函数内部的程序语句(函数体)没有被立即执行
- 2.特殊函数被调用后,会返回一个协程对象(但是函数本身的返回值则不会返回!若要返回需调用回调函数,后面会讲)
协程:
- 协程对象,特殊函数调用后就可以返回/创建了一个协程对象。
#特殊函数get_requests(url)
c = get_requests(url)
- 协程对象 == 特殊的函数 == 一组指定形式的操作
- 协程对象 == 一组指定形式的操作
任务:
- 任务对象就是一个高级的协程对象。高级之处,后面讲,不着急!
#接受任务对象
task = asyncio.ensure_future(c)
- 任务对象 == 协程对象 == 一组指定形式的操作
- 任务对象 == 一组指定形式的操作
事件循环:
#延续之前代码
#创建事件循环
loop = asyncio.get_event_loop()
#执行任务对象
loop.run_until_complete(task)
- 事件循环对象(Event Loop),可以将其当做是一个容器,该容器是用来装载任务对象的。所以说,让创建好了一个或多个任务对象后,下一步就需要将任务对象全部装载在事件循环对象中。
- 思考:为什么需要将任务对象装载在事件循环对象?
- 当将任务对象装载在事件循环中后,启动事件循环对象,则其内部装载的任务对象对应的相关操作就会被立即执行。
任务对象对比协程对象的高级之处重点在于:
可以给任务对象绑定一个回调函数!
回调函数有什么作用?
回调函数就是回头调用的函数,因此要这么理解,当任务对象被执行结束后,会立即调用给任务对象绑定的这个回调函数!
import asyncio
import time
async def get_request(url):
print('正在请求的网址是:',url)
time.sleep(2)
print('请求网址结束!')
return 123
#如何获取特殊函数内部的返回值(任务对象回调函数来实现的)
c = get_request('www.1.com')
task = asyncio.ensure_future(c)
#给任务对象绑定一个回调函数(回头调用的函数),该函数一定是在任务对象被执行完毕后再调用的函数
def task_callback(t): #必须有且仅有一个参数
#函数的参数t就是回调函数的调用者task任务对象本身
ret = t.result() #任务对象调用result()就可以返回特殊函数的内部return后的结果值
print('我是回调函数,我被执行了,t.result()返回的结果是:',ret)
#给task任务对象绑定了一个叫做task_callback的回调函数
task.add_done_callback(task_callback)
loop = asyncio.get_event_loop()
loop.run_until_complete(task)