协程不是由计算机提供,而是程序员人为创造的。
协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术吗,简而言之,其实就是通过一个线程实现代码块相互切换执行。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2022/3/17 19:38
# @Author : camellia
# @Email : 805795955@qq.com
# @File : coroutine.py
# @Software: PyCharm
# 测试协程程序
def fun1():
print('我在方法fun1中!')
def fun2():
print('我在方法fun2中……')
fun1()
fun2()
实现协程大概有以下这么几种方法:
1. Greenlet,早期模块
2. Yield关键字
3. Asyncio装饰器(python3.4以上才有)
4. Async await关键字(python3.5以上才有)
一:greenlet(比较老旧,不建议使用)
1:安装greenlet
pip3 install greenlet
2:基本使用
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2022/3/17 19:38
# @Author : stone
# @Email : 805795955@qq.com
# @File : coroutine.py
# @Software: PyCharm
# 测试协程程序
from greenlet import greenlet
def test1():
print(12) # 第二步 输出12
gr2.switch() # 第三步 切换test2函数
print(34) # 第六步 输出34
gr2.switch() # 第七步 切换test2函数
def test2():
print(56) # 第四步 输出56
gr1.switch() # 第五步 切换test1函数
print(78) # 第八步 输出78
gr1.switch()
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch() # 第一步 执行test1函数
执行上边的代码,输出:
12
56
34
78
具体输出步骤请参照代码中的注释。
二:yield关键字(生成器)(比较老旧,不建议使用)
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2022/3/17 19:38
# @Author : camellia
# @Email : 805795955@qq.com
# @File : coroutine.py
# @Software: PyCharm
# 测试协程程序
def fun1():
yield 1 # 第二步 输出 1
yield from fun2() # 第三步调用fun2
yield 2 # 第六步 输出 2
def fun2():
yield 3 # 第四步 输出 3
yield 4 # 第五步 输出 4
f1 = fun1() # 第一步调用fun1
for item in f1:
print(item)
执行上边代码输出:
1
3
4
2
具体输出步骤请参照代码中的注释。
三:asyncio(>=python3.4)
Python3.4以上的版本还可以使用asyncio来实现协程,具体代码如下:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2022/3/17 19:38
# @Author : camellia
# @Email : 805795955@qq.com
# @File : coroutine.py
# @Software: PyCharm
# 测试协程程序
import asyncio
@asyncio.coroutine
def fun1():
print(1)
yield from asyncio.sleep(1)
print(2)
@asyncio.coroutine
def fun2():
print(3)
yield from asyncio.sleep(1)
print(4)
# 任务列表
tasks = [
asyncio.ensure_future(fun1()),
asyncio.ensure_future(fun2())
]
# 时间循环
loop = asyncio.get_event_loop()
# 执行
loop.run_until_complete(asyncio.wait(tasks))
执行上边的代码,输出:
1
3
2
4
Asyncio这个就比较厉害了,他是自动切换执行方法的。上边我们看到的yield与greenled都是我们人为控制的。这就很高级。
四:async await关键字(>=python3.5)(推荐使用)
Python3.5以上才可以使用async与await,这两个关键字其实就是替代我们上边使用到的yield关键字,因此我们只需要将上方的程序做一下小修改即可:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2022/3/17 19:38
# @Author : camellia
# @Email : 805795955@qq.com
# @File : coroutine.py
# @Software: PyCharm
# 测试协程程序
import asyncio
async def fun1():
print(1)
await asyncio.sleep(1)
print(2)
async def fun2():
print(3)
await asyncio.sleep(1)
print(4)
# 任务列表
tasks = [
asyncio.ensure_future(fun1()),
asyncio.ensure_future(fun2())
]
# 时间循环
loop = asyncio.get_event_loop()
# 执行
loop.run_until_complete(asyncio.wait(tasks))
以上大概就是python中四种实现协程的方式。
五:协程的意义
在线程中如果遇到IO等待时间,线程不会去傻等,利用空闲的时候再去做点其他的事。
干说没有代码实现来的醒目。下边我们使用一个例子来展示一下:
下载三张图片,第一种实现方式,使用正常的同步下载。
第二种实现方式使用协程来下载。对比下载时间。
同步爬取:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2022/3/17 19:38
# @Author : camellia
# @Email : 805795955@qq.com
# @File : coroutine.py
# @Software: PyCharm
# 测试协程程序
import time
import requests
import random
import string
import asyncio
start =time.clock()
def getImage():
"""
:name 爬取网络图片
:param url: 图片url
:param num: 图片名称
:return: 无返回值
"""
lis = [
"https://resource.guanchao.site/uploads/sowing/welcome-image3.jpg",
"https://resource.guanchao.site/uploads/sowing/welcome-image7.jpg",
"https://resource.guanchao.site/uploads/sowing/welcome-image5.jpg"
]
for item in lis:
# 1:指定url
# url = "https://resource.guanchao.site/uploads/sowing/welcome-image3.jpg"
# 2:模拟网络请求链接
responce = requests.get(url=item)
print('爬取:'+item)
# 3:获取响应数据,content获取二进制数据
content = responce.content
filename = './img/' + ''.join(random.sample(string.ascii_letters + string.digits, 8)) + '.jpg'
# 4:持久化存储
with open(filename, 'wb') as fe:
fe.write(content)
print('爬取完成')
getImage()
end = time.clock()
print('Running time: %s Seconds'%(end-start))
输出:
爬取:https://resource.guanchao.site/uploads/sowing/welcome-image3.jpg
爬取完成
爬取:https://resource.guanchao.site/uploads/sowing/welcome-image7.jpg
爬取完成
爬取:https://resource.guanchao.site/uploads/sowing/welcome-image5.jpg
爬取完成
Running time: 2.4804463 Seconds
协程爬取:
协程爬取使用aiohttp模块
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2022/3/17 19:38
# @Author : camellia
# @Email : 805795955@qq.com
# @File : coroutine.py
# @Software: PyCharm
# 测试协程程序
import time
import requests
import random
import string
import asyncio
import aiohttp
start =time.clock()
async def fetch(session,url):
print('发送请求:'+url)
async with session.get(url,verify_ssl=False) as response:
content = await response.content.read()
filename = './img/' + ''.join(random.sample(string.ascii_letters + string.digits, 8)) + '.jpg'
# 4:持久化存储
with open(filename, 'wb') as fe:
fe.write(content)
print('爬取完成')
async def mains():
async with aiohttp.ClientSession() as session:
lis = [
"https://resource.guanchao.site/uploads/sowing/welcome-image3.jpg",
"https://resource.guanchao.site/uploads/sowing/welcome-image7.jpg",
"https://resource.guanchao.site/uploads/sowing/welcome-image5.jpg"
]
tasks = [asyncio.create_task(fetch(session,url)) for url in lis]
await asyncio.wait(tasks)
# asyncio.run(mains())
loop = asyncio.get_event_loop()
loop.run_until_complete(mains())
执行代码:
发送请求:https://resource.guanchao.site/uploads/sowing/welcome-image3.jpg
发送请求:https://resource.guanchao.site/uploads/sowing/welcome-image7.jpg
发送请求:https://resource.guanchao.site/uploads/sowing/welcome-image5.jpg
爬取完成
爬取完成
爬取完成
Running time: 1.8801726 Seconds
我们可以根据 输出的文字顺序看到代码是如何执行的。并且执行速度更快。
有好的建议,请在下方输入你的评论。