python异步IO编程(一)

基础概念

协程:python  generator与coroutine

异步IO (async IO):一种由多种语言实现的与语言无关的范例(或模型)。

asyncio:Python 3.4版本引入的标准库,直接内置了对异步IO的支持。

异步IO

线程,多线程

多线程善于处理I/O密集型任务。
多进程擅长处理计算密集型(CPU-bound)任务:强密集循环和数学计算都属于此类。
并发是并行的一种特殊类型(或者说子类),多线程是并发的表现形式,多进程是并行的表现形式。
Python通过它的包 multiprocessing,threading 和 concurrent.futures 已经对这两种形式都提供了长期的支持。

异步IO

异步IO是一种单进程、单线程的设计:它使用协同多任务处理机制,是以协程为核心的一种编程模型。
异步IO并不是新发明的概念,它已经存在或正在被构建到其他语言及运行时环境中,如 Go,C# 和 Scala 等。
异步IO模型异步IO采用消息循环的模式,在消息循环中,主线程不断地重复“读取消息-处理消息”这一过程:

loop = get_event_loop() //实例消息队列
while True:
event = loop.get_event() //从队列中读取消息
process_event(event)    //处理消息消息模型其实早在应用在桌面应用程序中了。一个GUI程序的主线程就负责不停地读取消息并处理消息。所有的键盘、鼠标等消息都被发送到GUI程序的消息队列中,然后由GUI程序的主线程处理。

由于GUI线程处理键盘、鼠标等消息的速度非常快,所以用户感觉不到延迟。某些时候,GUI线程在一个消息处理的过程中遇到问题导致一次消息处理时间过长,此时,用户会感觉到整个GUI程序停止响应了,敲键盘、点鼠标都没有反应。
这种情况说明在消息模型中,处理一个消息必须非常迅速,否则,主线程将无法及时处理消息队列中的其他消息,导致程序看上去停止响应。
消息模型是如何解决同步IO必须等待IO操作这一问题的呢?当遇到IO操作时,代码只负责发出IO请求,不等待IO结果,然后直接结束本轮消息处理,进入下一轮消息处理过程。当IO操作完成后,将收到一条“IO完成”的消息,处理该消息时就可以直接获取IO操作结果。
在“发出IO请求”到收到“IO完成”的这段时间里,同步IO模型下,主线程只能挂起,但异步IO模型下,主线程并没有休息,而是在消息循环中继续处理其他消息。这样,在异步IO模型下,一个线程就可以同时处理多个IO请求,并且没有切换线程的操作。对于大多数IO密集型的应用程序,使用异步IO将大大提升系统的多任务处理能力。

asyncio

async与asyncio.coroutine

在 python3.5 中,创建一个协程仅仅只需使用 async 关键字,而python3.4使用 @asyncio.coroutine 装饰器。都引入了原生协程或者说异步生成器。下面的任一代码,都可以作为协程工作,形式上也是等同的:

import asyncio

async def ping_server(ip): # 3.5
pass

@asyncio.coroutine
def load_file(path): # 3.4
pass

yield from与await

3.1 中协程操作只是简单的生成器调用,常见的我们还需要在生成器或者说协程之间相互调用,用到yield from。yield from 用于一个generator调用另一个generator,主要是为了generator之间的调用。
yield from 表达式的使用方式如下:

import asyncio
@asyncio.coroutine
def get_jason(client, url):
file_content = yield from load_file('/Usrs/scott/data.txt')

协程的一个关键特性是它们可以被链接到一起。(记住,一个协程是可等待的,所以另一个协程可以使用 await 来等待它。)await 将控制器传递给时间循环。(挂起当前运行的协程与yield from类似),使用方式如下:

async def ping_local(ip):
  return await ping_server('192.168.1.1')

Python3.5 对这两种调用协程的方法都提供了支持,但是推荐 async/await 作为首选。

Python执行的时候, g() 函数范围内如果遇到表达式 await f(),就是 await 在告诉事件循环“挂起 g() 函数,直到 f() 返回结果,在此期间,可以运行其他函数。”

async def g():
#暂停,直到 f()结束再回到g()
  r = await f()
  return r

当你使用 await f() 时,要求 f() 是一个可等待的对象。但这并没有什么用。现在,只需要知道可等待对象要么是(1)其他的协程,要么就是(2)定义了 .await() 函数且返回迭代器的对象。如果你正在编写程序,绝大多数情况只需要关注案例#1。

使用规则

1. 使用 await 与 return 的组合创建协程函数。想要调用一个协程函数,必须使用 await 等待返回结果。
2. 在 async def 代码块中使用 yield 的情况并不多见(只有Python的近期版本才可用)。当你使用 async for 进行迭代的时候,会创建一个异步生成器。暂时先忘掉异步生成器,将目光放在使用 await 与 return 的组合创建协程函数的语法上。
3. 在任何使用 async def 定义的地方都不可以使用 yield from,这会引发异常 SyntaxError。
4. 一如在 def 定义的函数之外使用 yield 会引发异常 SyntaxError,在 async def 定义的协程之外使用 await 也会引发异常 SyntaxError。你只能在协程内部使用 await。

Event Loop

asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。

用asyncio实现Hello world代码如下:

import asyncio

@asyncio.coroutine
def hello():
  print("Hello world!")
# 异步调用asyncio.sleep(1):
  r = yield from asyncio.sleep(1)
  print("Hello again!")

# 获取EventLoop:
loop = asyncio.get_event_loop()
# 执行coroutine
loop.run_until_complete(hello())
loop.close()

@asyncio.coroutine把一个generator标记为coroutine类型,然后,我们就把这个coroutine扔到EventLoop中执行。
hello()会首先打印出Hello world!,然后,yield from语法可以让我们方便地调用另一个generator。由于asyncio.sleep()也是一个coroutine,所以线程不会等待asyncio.sleep(),而是直接中断并执行下一个消息循环。当asyncio.sleep()返回时,线程就可以从yield from拿到返回值(此处是None),然后接着执行下一行语句。
把asyncio.sleep(1)看成是一个耗时1秒的IO操作,在此期间,主线程并未等待,而是去执行EventLoop中其他可以执行的coroutine了,因此可以实现并发执行。

async/await版本:

import asyncio

async def hello():
print("Hello World")
r = await asyncio.sleep(1)
print("Again")

loop = asyncio.get_event_loop()
loop.run_until_complete(hello())
loop.close()

我们用Task封装两个coroutine试试:

import threading
import asyncio

@asyncio.coroutine
def hello():
  print('Hello world! (%s)' % threading.currentThread())
yield from asyncio.sleep(1)
  print('Hello again! (%s)' % threading.currentThread())

loop = asyncio.get_event_loop()
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

观察执行过程:
Hello world! (<_MainThread(MainThread, started 140735195337472)>)
Hello world! (<_MainThread(MainThread, started 140735195337472)>)
(暂停约1秒)
Hello again! (<_MainThread(MainThread, started 140735195337472)>)
Hello again! (<_MainThread(MainThread, started 140735195337472)>)

由打印的当前线程名称可以看出,两个coroutine是由同一个线程并发执行的。
如果把asyncio.sleep()换成真正的IO操作,则多个coroutine就可以由一个线程并发执行。

参考:

https://mp.weixin.qq.com/s/fJaXmfHfYEk6XL2y8NmKmQ

https://www.jianshu.com/p/2dfaacdd0a90

python异步IO编程(一)的更多相关文章

  1. Python异步IO --- 轻松管理10k+并发连接

    前言   异步操作在计算机软硬件体系中是一个普遍概念,根源在于参与协作的各实体处理速度上有明显差异.软件开发中遇到的多数情况是CPU与IO的速度不匹配,所以异步IO存在于各种编程框架中,客户端比如浏览 ...

  2. Python - 异步IO\数据库\队列\缓存

    协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程,协程一定是在单线程运行的. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和 ...

  3. Python笔记-IO编程

    IO在计算机中是指input和output(数据输入与输出),涉及到数据交换(磁盘.网络)的地方就需要IO接口. 输入流input stream是指数据从外面(磁盘.网络服务器)流入内存:输出流out ...

  4. Python之IO编程——文件读写、StringIO/BytesIO、操作文件和目录、序列化

    IO编程 IO在计算机中指Input/Output,也就是输入和输出.由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘.网络等,就需要IO接口.从 ...

  5. Python异步IO

    在IO操作的过程中,当前线程被挂起,而其他需要CPU执行的代码就无法被当前线程执行了. 我们可以使用多线程或者多进程来并发执行代码,为多个用户服务. 但是,一旦线程数量过多,CPU的时间就花在线程切换 ...

  6. python -- 异步IO 协程

    python 3.4 >>> import asyncio >>> from datetime import datetime >>> @asyn ...

  7. 【Python】IO编程

    文件读写 StringIO和BytesIO 操作文件和目录 序列化 学习廖老师的py官网的笔记 1.stream的概念.数据交换通常需要建立两根“水管”. 2.同步IO和异步IO.异步性能高,但是编程 ...

  8. Python之IO编程

    前言:由于程序和运行数据是在内存中驻留的,由CPU这个超快的计算核心来执行.当涉及到数据交换的地方,通常是磁盘.网络等,就需要IO接口.由于CPU和内存的速度远远高于外设的速度,那么在IO编程中就存在 ...

  9. Python 异步IO、IO多路复用

    事件驱动模型 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...

随机推荐

  1. python之最强王者(5)——Nunber(数字)

    1.Python Number(数字) Python Number 数据类型用于存储数值. 数据类型是不允许改变的,这就意味着如果改变 Number 数据类型的值,将重新分配内存空间. 以下实例在变量 ...

  2. c#处理空白字符

    空白字符是指在屏幕不会显示出来的字符(如空格,制表符tab,回车换行等).空格.制表符.换行符.回车.换页垂直制表符和换行符称为 “空白字符”,因为它们为与间距单词和行在打印的页 )的用途可以读取更加 ...

  3. MyBatis框架——mybatis插入数据返回主键(mysql、oracle)

    向数据库中插入数据时,大多数情况都会使用自增列或者UUID做为主键.主键的值都是插入之前无法知道的,但很多情况下我们在插入数据后需要使用刚刚插入数据的主键,比如向两张关联表A.B中插入数据(A的主键是 ...

  4. 为 UWP 应用提供的 .NET 网络 API

    [编者按]本文作者是 Windows networking 团队项目经理 Sidharth Nabar.在微软 Build 2015 大会上,.NET Core 5 作为开发 UWP(Universa ...

  5. C#一些小技巧

    在C#实现类似Typedef的所有功能 Typedef这个关键字,是比较好用的东西,因为有时候我们需要使用一些别名来帮助我们记忆某些结构体或者类的共用.(个人觉得这是C与C++唯一能吸引我的东西)为了 ...

  6. appendGrid

    appendGrid appendGrid的使用

  7. servlet核心API的UML图

  8. CCIE-MPLS VPN-实验手册(中卷)

    5:MPLS VPN PE CE OSPF 实验1 5.1 实验拓扑 5.2 实验需求 a. R1 R2 R3 组成P-NETWORK,底层协议采用EIGRP b. R1 R2 R3 直连链路启用LD ...

  9. HDU5661 Claris and XOR

    我们求二进制是怎么求的呢:先看看二进制的每一位代表多大:.......32 16 8 4 2 1 假如n=10, ..... 32>n ,不要. 16>n,不要. 8<=n,要,然后 ...

  10. TensorFlow迭代速度变慢的问题

    最近用TensorFlow实现遗传算法(Genetic Algorithms),发现迭代速度越来越慢,用time.time()观察以后,发现每次迭代都要比上一次慢0.5秒左右,但是每次迭代的计算量是差 ...