Python下异步IO和协程简介
2015-10-19 19:48
459 查看
1、协程(coroutine)
协程(coroutine)也是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。一个程序可以包含多个协程,可以对比与一个进程包含多个线程,因而下面我们来比较协程和线程。我们知道多个线程相对独立,有自己的上下文,切换受系统控制;而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程由当前协程来控制。
简而言之:协程是一种用户态的轻量级线程。
假设A、B是同一线程下的两个协程,则这两个协程之间可以任意的切换,最重要的是解决了线程切换的阻塞问题(这也是为什么协程和异步IO通常在一起被提到)。协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。另外,由于不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
2、异步IO(asyncio)
在一个线程中,CPU执行代码的速度极快,然而,一旦遇到IO操作,如读写文件、发送网络数据时,就需要等待IO操作完成,才能继续进行下一步操作。这种情况称为同步IO。
在IO操作的过程中,当前线程被挂起,而其他需要CPU执行的代码就无法被当前线程执行了。
因为一个IO操作就阻塞了当前线程,导致其他代码无法执行,所以我们必须使用多线程或者多进程来并发执行代码,为多个用户服务。每个用户都会分配一个线程,如果遇到IO导致线程被挂起,其他用户的线程不受影响。
多线程和多进程的模型虽然解决了并发问题,但是系统不能无上限地增加线程。由于系统切换线程的开销也很大,所以,一旦线程数量过多,CPU的时间就花在线程切换上了,真正运行代码的时间就少了,结果导致性能严重下降。
由于我们要解决的问题是CPU高速执行能力和IO设备的龟速严重不匹配,多线程和多进程只是解决这一问题的一种方法。
另一种解决IO问题的方法是异步IO。当代码需要执行一个耗时的IO操作时,它只发出IO指令,并不等待IO结果,然后就去执行其他代码了。一段时间后,当IO返回结果时,再通知CPU进行处理。
3、代码举例
输出结果:
从结果可以看出,两个协程在同一线程中
协程(coroutine)也是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。一个程序可以包含多个协程,可以对比与一个进程包含多个线程,因而下面我们来比较协程和线程。我们知道多个线程相对独立,有自己的上下文,切换受系统控制;而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程由当前协程来控制。
简而言之:协程是一种用户态的轻量级线程。
假设A、B是同一线程下的两个协程,则这两个协程之间可以任意的切换,最重要的是解决了线程切换的阻塞问题(这也是为什么协程和异步IO通常在一起被提到)。协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。另外,由于不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
2、异步IO(asyncio)
在一个线程中,CPU执行代码的速度极快,然而,一旦遇到IO操作,如读写文件、发送网络数据时,就需要等待IO操作完成,才能继续进行下一步操作。这种情况称为同步IO。
在IO操作的过程中,当前线程被挂起,而其他需要CPU执行的代码就无法被当前线程执行了。
因为一个IO操作就阻塞了当前线程,导致其他代码无法执行,所以我们必须使用多线程或者多进程来并发执行代码,为多个用户服务。每个用户都会分配一个线程,如果遇到IO导致线程被挂起,其他用户的线程不受影响。
多线程和多进程的模型虽然解决了并发问题,但是系统不能无上限地增加线程。由于系统切换线程的开销也很大,所以,一旦线程数量过多,CPU的时间就花在线程切换上了,真正运行代码的时间就少了,结果导致性能严重下降。
由于我们要解决的问题是CPU高速执行能力和IO设备的龟速严重不匹配,多线程和多进程只是解决这一问题的一种方法。
另一种解决IO问题的方法是异步IO。当代码需要执行一个耗时的IO操作时,它只发出IO指令,并不等待IO结果,然后就去执行其他代码了。一段时间后,当IO返回结果时,再通知CPU进行处理。
3、代码举例
# -*- coding:utf-8 -*- __auth__ = 'peic' ''' Python 协程和异步IO yield 、yield from的使用 asyncio 模块的使用 ''' # -*- 协程 -*- # 首先需要理解yield # 函数中有yield语句而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行,类似CPU的中断处理 def consumer(): c_r = '' while True: # 此处yield接受调用者发出的参数,通过send进行调用 # c.send(p_n)中n的值通过yield返回,赋值给r_n # send(value):The value argument becomes the result of the current yield expression. # consumer通过yield拿到消息,处理,又通过yield把结果传回。拿到的值是send传递的值,赋给c_n,返回的值是c_r c_n = yield c_r if not c_n: return print('[CONSUMER] Consuming %s...' % c_n) c_r = '200 OK' def produce(c): c.send(None) p_n = 0 while p_n < 5: p_n = p_n + 1 print('[PRODUCER] Producing %s...' % p_n) # 调用send的generator(此处就是c),send语句会将参数(也就是p_n)的值传给这个生成器目前yield表达式的值(c_n) # 而send表达式的值(也就是传给p_r的值)会是generator的下一个值(next(c_r)) # 通过调用返回的形式,c.send()就完成了函数的调用返回,即执行了一步generator(consumer)然后返回到原函数(produce),在这个过程中,yield起到中断返回作用 p_r = c.send(p_n) print('[PRODUCER] Consumer return: %s' % p_r) c.close() c = consumer() produce(c) # -*- 异步IO -*- import asyncio import threading # @asyncio.coroutine把一个generator标记为coroutine类型 @asyncio.coroutine def sub(): print('sub start: ...') n = 10 while True: print('yield start') # asyncio.sleep()也是一个coroutine类型的generator,所以线程不会中断,而是直接执行下一个循环,等待yield from的返回 # 可以简单的理解为出现yield之后则开启一个协程(类似开启一个新线程),不管这个协程是否执行完毕,继续下一个循环 # 开启新协程后,print('yield start')会因为继续执行循环被立即执行,可以通过打印结果观察 r = yield from asyncio.sleep(1) n = n - 1 print('---sub: %s, thread:%s' %(n, threading.currentThread())) if n == 0: break @asyncio.coroutine def add(): print('add start: ...') n = 10 while True: print('yield start') r = yield from asyncio.sleep(2) n = n + 1 print('+++add: %s, thread:%s' %(n, threading.currentThread())) if n > 20: break # 获取EventLoop: loop = asyncio.get_event_loop() # 执行coroutine tasks = [add(),sub()] loop.run_until_complete(asyncio.wait(tasks)) loop.close()
输出结果:
[PRODUCER] Producing 1... [CONSUMER] Consuming 1... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [PRODUCER] Consumer return: 200 OK add start: ... sub start: ... ---sub: 9, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 11, thread:<_MainThread(MainThread, started 140436577416960)> ---sub: 8, thread:<_MainThread(MainThread, started 140436577416960)> ---sub: 7, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 12, thread:<_MainThread(MainThread, started 140436577416960)> ---sub: 6, thread:<_MainThread(MainThread, started 140436577416960)> ---sub: 5, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 13, thread:<_MainThread(MainThread, started 140436577416960)> ---sub: 4, thread:<_MainThread(MainThread, started 140436577416960)> ---sub: 3, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 14, thread:<_MainThread(MainThread, started 140436577416960)> ---sub: 2, thread:<_MainThread(MainThread, started 140436577416960)> ---sub: 1, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 15, thread:<_MainThread(MainThread, started 140436577416960)> ---sub: 0, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 16, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 17, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 18, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 19, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 20, thread:<_MainThread(MainThread, started 140436577416960)> +++add: 21, thread:<_MainThread(MainThread, started 140436577416960)>
从结果可以看出,两个协程在同一线程中
相关文章推荐
- Python中assert的用法
- python+Eclipse+pydev环境搭建
- python排序算法和实现
- Python技巧——list与字符串互相转换
- 定位 python image not found问题
- python字符串操作,以及截取对应的C#函数
- Python 在Visual studio 中做单元测试进行TDD开发
- 模拟登录新浪微博(Python) - 转
- Eclipse环境安装Python插件PyDev
- python实现ssh自动化登录遇到的坑
- Python快速教程
- Spark SQL Table Join(Python)
- Python 自定义模块位置
- python函数基础知识
- Python爬虫--抓取糗事百科段子
- Python scrapy run with Unhandled error in Deferred解决方案
- Python3包机制
- python打印中文字典
- python pandas库基础
- python list中delete符合条件的元素