异步编程

2022-09-20 19:16:38 浏览数 (1)

五一快乐呀(5.10日:对不起我卡住了太久),由于我玩了三天所以没怎么更新,我的干货也发完啦。现在开始学习新的东西了,异步编程,觉得有点儿难,有些东西理解不了,话说现在我的学习进度很慢,需要加快点速度了。

异步编程的含义是什么呢,如果程序调用某个方法,等待其执行全部处理后才能继续执行,我们称其为同步的。相反,在处理完成之前就返回调用方法则是异步的,异步编程可以大大提高的性能。

实现协程的方式:

1.greenlet,早期模块

2.yield关键字

3.asyncio装饰器

4.async,await关键字

现在我要学习的是asyncio这种方式(话说有点难,我借助CSDN编辑的)

在使用之前需要导一下asyncio的包

代码语言:javascript复制
import asyncio

我们要定义一个协程函数,只要在我们平常定义时的def前面加上一个async即可,要调用异步函数,必须使用await关键字,await后面接的是可等待对象,分别为coroutine对象、task对象,future对象。

1.事件循环

管理所有的事件,在整个程序运行过程中不断循环执行并追踪事件发生的顺序将它们放在队列中,空闲时调用相应的事件处理者来处理这些事件。

2.Future

Future对象表示尚未完成的计算,还未完成的结果

3.Task

是Future的子类,作用是在运行某个任务的同时可以并发的运行多个任务。

asyncio.Task用于实现协作式多任务的库,且Task对象不能用户手动实例化,通过下面2个函数创建:

asyncio.async()

loop.create_task()asyncio.ensure_future()

代码语言:javascript复制
async def function():

现在来模拟一个协程函数,如果我们直接运行fun()这个函数,是得不到结果的,还会报错。

代码语言:javascript复制
import asyncio


async def fun(a, b):
    await asyncio.sleep(2)
    return a   b


result = fun(2, 3)
print(result)

# 运行结果:
<coroutine object fun at 0x00000263F6EC3748>
sys:1: RuntimeWarning: coroutine 'fun' was never awaited

如果要运行一个协程函数,需要将函数放在事件循环中运行。

代码语言:javascript复制
loop=asyncio.get_event_loop()    #创建事件循环对象
#loop=asyncio.new_event_loop()   #与上面等价,创建新的事件循环
loop.run_until_complete(fun())  #通过事件循环对象运行协程函数
loop.close()

python3.7以后的写法:
asyncio.run(fun())  # 与上面的等价

再次运行,结果不同

代码语言:javascript复制
import asyncio


async def fun(a, b):
    await asyncio.sleep(2)
    return a   b


result = asyncio.run(fun(2, 3))
print(result)  # 5

现在来执行一个比较简单的协程函数,这个await后面接的应该是coroutine(协程)函数对象。

代码语言:javascript复制
async def other():
    print("start")
    await asyncio.sleep(2)
    print("end")
    return ("返回值")


async def run():
    print("执行协程函数内部代码")
    response = await other()  # 会去执行other函数,response接收other函数的返回值
    print("执行结束,结果为", response)


asyncio.run(run())

await类似于yield from,await的作用就是阻塞调用方,并获取await xxx()中xxx的返回值。

asyncio.create_task()以异步方式同时运行协程的函数Tasks。这个await后面接的是task对象。

代码语言:javascript复制
import asyncio
import time

#asyncio.create_task()以异步方式同时运行协程的函数Tasks。

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    task1 = asyncio.create_task(
        say_after(2, 'hello'))

    task2 = asyncio.create_task(
        say_after(3, 'world'))

    task3 = asyncio.create_task(
        say_after(4, '!'))


    print(f"started at {time.strftime('%X')}")

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await task1
    await task2
    await task3

    print(f"finished at {time.strftime('%X')}")
asyncio.run(main())

运行结果:
started at 18:43:32
hello
world
!
finished at 18:43:36

可见总共是用时4秒,是异步的效果,tasks是同时进行的。如果是用同步函数进行的话,用时将会是9秒。

唉,实在是难,以上就是我现在所学的内容。有点学不下去,感觉需要一些基础知识才行,学的好吃力啊,但是感觉我必须要学会它,一直在找资料边写边学,累了就休息睡觉,学的很慢很慢...

-----------------下午---------------

又学了一下午,对我来说真是太难了,找了超级多的文章在看,继续加油吧。

------------------晚上------------------

还在看资料....理解起来真的好累,看他们的实例代码也好累

头大中。。。慢慢看

–––––其实已经过了很久–––––––

有时候视频/资料看多了就真的看着看着就懂了,只能说略懂吧现在,晚点回去继续写剩下的,现在已经是5.10了,可见我挣扎了很久,但还是不想放弃。

回到寝室,开始回顾知识:

执行一个协程函数

这是旧版的写法,执行协程函数的方法是首先要创建一个事件循环(loop),然后将调用函数时得到的协程对象(c)注册到循环(loop)中。因为直接调用request函数是不会执行的,只会返回一个协程对象。写着写着发现自己更加能理解了!

代码语言:javascript复制
import asyncio

async def request(url):
    print("正在请求", url)
    print(url, "请求完成")
    return "返回值"

# async修饰的函数,调用之后返回一个协程对象,用c接收协程对象
c = request("www.baidu.com")

# 创建一个事件循环对象
loop = asyncio.get_event_loop()

# 将协程对象c注册到loop中,然后启动loop,才会执行request函数
loop.run_until_complete(c)

# 新版写法:
asyncio.run(c)

task的使用

代码语言:javascript复制
# 创建一个事件循环
loop = asyncio.get_event_loop()
# 基于loop创建一个任务
task = loop.create_task(c)
print("1:", task)  # 此时未执行
# 将任务注册到loop中
loop.run_until_complete(task)
print("2:", task)  # 此时已执行

future的使用

future本质上和task没太大区别,但是future的创建是不用基于loop的。

代码语言:javascript复制
# 创建循环
loop = asyncio.get_event_loop()
# future是基于asyncio方法创建的
task = asyncio.ensure_future(c)
# 执行协程函数
loop.run_until_complete(task)

回调函数

代码语言:javascript复制
async def request1(url):
    print("正在请求", url)
    print(url, "请求完成")
    return "返回值"

a = request1("www.baidu.com")
def callback1(task):
    # result返回的就是任务对象封装的协程对象对应函数返回的值
    print(task.result()) # 返回值

# 绑定回调
# 创建事件循环
loop = asyncio.get_event_loop()
# 创建任务对象
task = asyncio.ensure_future(a)
# 将回调函数绑定在任务对象中
task.add_done_callback(callback1)
# 将任务对象注册到loop中
loop.run_until_complete(task)

学习完这些基础知识以后可以模拟异步爬虫了。

代码语言:javascript复制
import asyncio
import time

async def request(url):
    print("loading ", url)
    # 如果出现了同步模块的代码,就无法实现异步
    await asyncio.sleep(2)  # 这里模拟IO等待
    print(url, " is downloaded")

start = time.time()

urls = [
    "www.baidu.com",
    "www.sogou.com"
]
tasks = []
for url in urls:
    c = request(url)
    task = asyncio.ensure_future(c)
    tasks.append(task)

loop = asyncio.get_event_loop()
# 需要将任务列表封装到asyncio.wait中

loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print(end - start)

由于requests模块是同步的模块,会直接中断异步协程的操作,所以涉及到别的模块,下一篇继续写,这一篇只能用sleep来模拟了。

0 人点赞