Python说文解字_Python之多任务_05

问:在Py3.5以前yield表现很是好,在Py3.5以后为了将予以变得更加明确,就引入了async和await关键词用于定义原生的协议。html

答:async和await原生协程:nginx

async def downloader(url):
    return "bobby"

async def downloader_url(url):
    # do something
    html = await downloader(url)
    return html

if __name__ == '__main__':
    coro = downloader_url("http://www.baidu.com")
    next(None)
    # coro.send(None)

# 1.若是调用send正常
# StopIteration: bobby
# 2.若是调用next草异常,所以原生协程只能用send
# TypeError: 'NoneType' object is not an iterator
# sys:1: RuntimeWarning: coroutine 'downloader_url' was never awaited

  咱们发现:原生协程只能用send不能用next。并且发现 原生协程和yield协程差很少,前面加上了async语法,await相似于yield from。Python引入了async和await原生协程是为了咱们的语义更加的清晰。若是咱们用生成器写出的协程的话,代码很是的凌乱的。由于它又能当生成器又能当协程,显得比较凌乱,将这两种区分开来。所以async里面是不能定义yield的。所以Python增强了咱们的区别。所以这两个是一对的。这样咱们的协程区分开来。前面说了那么多生成器就是为了增强协程的理解。这样咱们在协程里面就用这两个。所以在Python内部依然沿用了生成器的原理,来实现了咱们的协程。程序员

  await后跟随的Awaitbale对象。咱们能够经过from  collections import Awaitalbe模块。web

  其实这个是实现了魔法拿书中的__await__的方法,所以咱们还可使用装饰器的方法来操做,省去asyn,而变换成咱们熟悉的生成器的样子。代码以下:django

import types

@types.coroutine
def downloader(url):
    yield "bobby"

async def downloader_url(url):
    # do something
    html = await downloader(url)
    return html

if __name__ == '__main__':
    coro = downloader_url("http://www.baidu.com")
    # next(None)
    coro.send(None)

 

 

问:生成器是如何变成咱们协程的?编程

答:在开始我么引入过协程的需求,咱们的协程是经过单线程调度,协程是咱们函数级别的是由咱们程序员本身来决定调用的,咱们能够写同步代码同样写异步代码。咱们的生成器就能够完成咱们的协程的这么一个功能。咱们如今就能够用协程来模拟咱们的需求。flask

  生成器是能够暂停的函数,实际上生成器是能够有状态的!服务器

  咱们看这段代码多线程

import inspect
def gen_func():
    yield 1
    return "bobby"

if __name__ == '__main__':
    gen = gen_func()
    print(inspect.getgeneratorstate(gen)) # GEN_CREATED
    next(gen)
    print(inspect.getgeneratorstate(gen)) # GEN_SUSPENDED
    try:
        next(gen)
    except StopIteration:
        pass
    print(inspect.getgeneratorstate(gen)) # GEN_CLOSED

  经过inspect中的getgeneratorstate咱们来观察生成器的状态,实际上咱们在定义咱们的生成器的时候,生成器能够接收咱们的值。这句话有两个意思:第一是返回值给调用方,第二调用方经过send方式返回值跟gen。如今咱们生成器由“生产者”变为“消费者”。
并发

  1.咱们用同步的方式编写异步的代码。

  2.在适当的时候暂停函数,并在适当的时候启动函数。

  如今咱们模式:事件循环+协程模式

  咱们在函数当中的子函数,若是出现异常,会抛给这个函数的主函数,是“向上抛”的过程。这个就很好。协程是一个单线程模式

 

问:异步IO和IO复用,也就是同步IO和异步IO。

答:咱们对前面的东西略微作一个小结:

  异步IO和协程:如今咱们尚未把协程来用到咱们的编码当中,协程是须要事件循环来实现的。单独使用的话做用不是很明显。

  在最开始的时候我么说到了并发、并行、异步、同步、阻塞、非阻塞。

  在IO多路复用(同步IO)当中的select poll epoll,使咱们使用的最多的技术。回调+事件循环的方式。这种编程模式和同步IO的编程模式差异很大。

  所以这两种模式:回调+事件循环(IO多路复用)、协程+事件循环(异步IO)

  上面的编码是很是痛苦的:回调之痛。

  咱们引入了生成器和协程,协程并不会别上面的方式高,协程主要解决的问题是回调之痛的问题和编码习惯的问题。

  咱们能够将生成器编程咱们的协程了。

  最后引入了async和await来区别生成器和协程,不容易混乱,进行区分。咱们能够用Cororoutine装饰器的方式,就不要用了。

  因此建议使用async和await的方式。

 

问:async IO并发编程:

答:该模块是在Python3.4后引入的模块,这是Python编程中最难的部分。该模块也是Python最具野心的模块。分几个部分开始讲解:

  1. 事件循环:

  咱们能够把async IO看作一个模块也可看作一个框架,它完成了整套异步编程中最核心的内容。它包含各类特定系统实现的模块化事件循环,传输和协议抽象;对TCP,UDP,SSL,子进程,延时调用以及其余的具体支持;模仿futures模块但适用于事件循环使用Future类;基于yield from的协议和任务,可让你用顺序的方式编写并发代码;必须使用一个将产生阻塞IO的调用时,有接口能够把这个事件转移到线程池。能够将多进程和多线程协调进来。

  协程编码模式都逃离不掉三个要素:事件循环+调用(驱动生成器)+epoll(IO多路复用)

  asyncio 是Python用于解决异步IO编程的一整套解决方案。

  tornado、gevent、twisted(scrapy,django channels)

  tornado:实现了web服务器,djago+flask是Python最传统要搭配(uwsgi,gunicorn+nginx),tornado能够直接部署,nginx+tornado

 

  使用asyncio

import asyncio # 能够当作协程池来理解比较容易

import time


async def get_html(url):
    print("start get url")
    # time.sleep(2) # 阻塞式的IO不能写在里面
    await asyncio.sleep(2) # 不能使用import time,必需要加await
    print("end get url")

if __name__ == '__main__':
    start_time = time.time()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(get_html("htttp://www.baidu.com"))
    print(time.time() - start_time)

# start get url
# end get url
# 2.0150375366210938

 

   get_event_loop市价循环

  run_until_complete去执行

  这里不能用time.sleep这是阻塞式的方法。所以会单独的一个一个执行很是慢,因此要使用asynic中的sleep

import asyncio # 能够当作协程池来理解比较容易

import time


async def get_html(url):
    print("start get url")
    # time.sleep(2) # 阻塞式的IO不能写在里面
    await asyncio.sleep(2) # 不能使用import time,必需要加await
    print("end get url")

if __name__ == '__main__':
    start_time = time.time()
    loop = asyncio.get_event_loop()
    tasks = [get_html("htttp://www.baidu.com") for i in range(100)]
    loop.run_until_complete(asyncio.wait(tasks))
    print(time.time() - start_time)

# start get url
# end get url
# 2.0150375366210938
# time编程顺序执行。asyncio.sleep()能够当即执行。只要一个地方阻塞了其余方面都实现不了。

  咱们发现更改后就会阻塞。

 

import asyncio # 能够当作协程池来理解比较容易

import time


async def get_html(url):
    print("start get url")
    # time.sleep(2) # 阻塞式的IO不能写在里面
    await asyncio.sleep(2) # 不能使用import time,必需要加await
    return "bobby"
if __name__ == '__main__':
    start_time = time.time()
    loop = asyncio.get_event_loop()
    # get_future = asyncio.ensure_future(get_html("htttp://www.baidu.com"))
    # loop.create_task()
    # tasks = [get_html("htttp://www.baidu.com") for i in range(100)]
    task = loop.create_task(get_html("htttp://www.baidu.com"))
    loop.run_until_complete(task)
    print(time.time() - start_time)
    print(task.result())


# 获取协程的返回值

  咱们用协程调用线程池:ensure_funture

  使用方法还有create_task这两种都是比较好理解的。

 

import asyncio # 能够当作协程池来理解比较容易

import time
from functools import partial

async def get_html(url):
    print("start get url")
    # time.sleep(2) # 阻塞式的IO不能写在里面
    await asyncio.sleep(2) # 不能使用import time,必需要加await
    return "bobby"

def callback(url,future):
    print("send email to bobby")

if __name__ == '__main__':
    start_time = time.time()
    loop = asyncio.get_event_loop()
    # get_future = asyncio.ensure_future(get_html("htttp://www.baidu.com"))
    # loop.create_task()
    # tasks = [get_html("htttp://www.baidu.com") for i in range(100)]
    task = loop.create_task(get_html("htttp://www.baidu.com"))
    # task.add_done_callback(callback)
    task.add_done_callback(partial(callback,"htttp://www.baidu.com"))
    loop.run_until_complete(task)
    print(time.time() - start_time)
    print(task.result())


# 获取协程的返回值

 

  咱们也可使用回调,在task中的重写add_done_callback方法。

 

import asyncio # 能够当作协程池来理解比较容易

import time

async def get_html(url):
    print("start get url")
    # time.sleep(2) # 阻塞式的IO不能写在里面
    await asyncio.sleep(2) # 不能使用import time,必需要加await
    print("end get url")

if __name__ == '__main__':
    start_time = time.time()
    loop = asyncio.get_event_loop()
    tasks = [get_html("htttp://www.baidu.com") for i in range(100)]
    # loop.run_until_complete(asyncio.wait(tasks))
    loop.run_until_complete(asyncio.gather(*tasks))
    print(time.time() - start_time)

# wait 和 gather 的区别
# gather更加高层,能够将咱们task分组
    group1 = [get_html("htttp://www.baidu1.com") for i in range(100)]
    group2 = [get_html("htttp://www.baidu2.com") for i in range(100)]
    loop.run_until_complete(asyncio.gather(*group1,*group2))

    group1 = asyncio.gather(*group1)
    group2 = asyncio.gather(*group2)

    group2.cancel()

 

  咱们尽可能使用gather方法,注意他是能够将咱们task进行分组,后面要加上*参数的形式。

 

  2. task取消、嵌套、字写成调用原理

# import asyncio
#
# loop = asyncio.get_event_loop()
# loop.run_forever()
# loop.run_until_complete()
# 1.loop会被放到future中。
# 2.取消future(task)

 

import asyncio
import time

async def get_html(sleep_times):
    print("waiting")
    await asyncio.sleep(sleep_times)
    print("done after {}s".format(sleep_times))

if __name__ == '__main__':
    task1 = get_html(2)
    task2 = get_html(3)
    task3 = get_html(2)
    tasks = [task1,task2,task3]

    loop = asyncio.get_event_loop()

    try:
        loop.run_until_complete(asyncio.wait(tasks))
    except KeyboardInterrupt as e:
        all_task = asyncio.Task.all_tasks()
        for task in all_task:
            print("cancel task")
            task.cancel()
        loop.stop()
        loop.run_forever()
    finally:
        loop.close()

 

  3. call_soon() 即刻执行,call_later(),call_at()

 

  4.ThreadPoolExector   + asyncio

  使用多线程:在协程中继承阻塞io

  生成器 = ThreadPoolExecutor

  run_in+executor(生成器,函数,参数)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

11111

相关文章
相关标签/搜索