Python协程:从yield/send到async/await

这个文章理好了脉落。

http://python.jobbole.com/86069/

我练 习了一番,感受好了很多。。。

Python由于众所周知的GIL的原因,导致其线程无法发挥多核的并行计算能力(当然,后来有了multiprocessing,可以实现多进程并行),显得比较鸡肋。既然在GIL之下,同一时刻只能有一个线程在运行,那么对于CPU密集的程序来说,线程之间的切换开销就成了拖累,而以I/O为瓶颈的程序正是协程所擅长的:

多任务并发(非并行),每个任务在合适的时候挂起(发起I/O)和恢复(I/O结束)

弄清楚了asyncio.coroutine和yield from之后,在Python3.5中引入的async和await就不难理解了:可以将他们理解成asyncio.coroutine/yield from的完美替身。当然,从Python设计的角度来说,async/await让协程表面上独立于生成器而存在,将细节都隐藏于asyncio模块之下,语法更清晰明了。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import asyncio
import time
import random

'''
def old_fib(n):
    res = [0] * n
    index = 0
    a = 0
    b = 1
    while index < n:
        res[index] = b
        a, b = b, a + b
        index += 1
    return res

print("-"*10 + "test old fib " + "-"*10)
for fib_res in old_fib(20):
    print(fib_res)

def fib(n):
    index = 0
    a = 0
    b = 1
    while index < n:
        yield b
        a, b = b, a + b
        index += 1

print("-"*10 + "test yield fib " + "-"*10)
for fib_res in fib(20):
    print(fib_res)

def stupid_fib(n):
    index = 0
    a = 0
    b = 1
    while index < n:
        sleep_cnt = yield b
        print("let me think {0} secs".format(sleep_cnt))
        time.sleep(sleep_cnt)
        a, b = b, a + b
        index += 1
print("-"*10 + "test yield send " + "-"*10)
N = 20
sfib = stupid_fib(N)
fib_res = next(sfib)
while True:
    print(fib_res)
    try:
        fib_res = sfib.send(random.uniform(0, 0.5))
    except StopIteration:
        break

def copy_fib(n):
    print("I am copy from fib")
    yield from fib(n)
    print("copy end")
print("-"*10 + "test yield from " + "-"*10)
for fib_res in copy_fib(20):
    print(fib_res)

def copy_stupid_fib(n):
    print("I am copy from stupid fib")
    yield from stupid_fib(n)
    print("Copy end")

print("-"*10 + "test yield from and send" + "-"*10)
N = 20
csfib = copy_stupid_fib(N)
fib_res = next(csfib)
while True:
    print(fib_res)
    try:
        fib_res = csfib.send(random.uniform(0, 0.5))
    except StopIteration:
        break

@asyncio.coroutine
def smart_fib(n):
    index = 0
    a = 0
    b = 1
    while index < n:
        sleep_secs = random.uniform(0, 0.2)
        yield from asyncio.sleep(sleep_secs)
        print("Smart one think {} secs to get {}".format(sleep_secs, b))
        a, b = b, a + b
        index += 1

@asyncio.coroutine
def stupid_fib(n):
    index = 0
    a = 0
    b = 1
    while index < n:
        sleep_secs = random.uniform(0, 0.4)
        yield from asyncio.sleep(sleep_secs)
        print("Stupid one think {} secs to get {}".format(sleep_secs, b))
        a, b = b, a + b
        index += 1

loop = asyncio.get_event_loop()
tasks = [
    asyncio.async(smart_fib(10)),
    asyncio.async(stupid_fib(10)),
    ]
loop.run_until_complete(async.wait(tasks))
print("All fib finished.")
loop.close()

'''

async def smart_fib(n):
    index = 0
    a = 0
    b = 1
    while index < n:
        sleep_secs = random.uniform(0, 0.2)
        await asyncio.sleep(sleep_secs)
        print("Smart one think {} secs to get {}".format(sleep_secs, b))
        a, b = b, a + b
        index += 1

async def stupid_fib(n):
    index = 0
    a = 0
    b = 1
    while index < n:
        sleep_secs = random.uniform(0, 0.4)
        await asyncio.sleep(sleep_secs)
        print("Stupid one think {} secs to get {}".format(sleep_secs, b))
        a, b = b, a + b
        index += 1

loop = asyncio.get_event_loop()
tasks = [
    asyncio.async(smart_fib(10)),
    asyncio.async(stupid_fib(10)),
    ]
loop.run_until_complete(asyncio.wait(tasks))
print("All fib finished.")
loop.close()

Python中的协程经历了很长的一段发展历程。其大概经历了如下三个阶段:

  1. 最初的生成器变形yield/send
  2. 引入@asyncio.coroutine和yield from
  3. 在最近的Python3.5版本中引入async/await关键字
时间: 2024-08-19 20:59:56

Python协程:从yield/send到async/await的相关文章

理解Python的协程机制-Yield

根据PEP-0342 Coroutines via Enhanced Generators,原来仅仅用于生成器的yield关键字被扩展,成为Python协程实现的一部分.而之所以使用协程,主要是出于性能的考虑:一个活跃的Python线程大约占据8MB内存,而一个活跃线程只使用1KB不到内存.对于IO密集型的应用,显然轻量化的协程更适用. 概述 原来,yield是一个statement,即和return一样的语句,但是在PEP-0342后,yield statement被改造为了yield exp

谈谈Python协程技术的演进

一.引言 1. 存储器山 存储器山是 Randal Bryant 在<深入理解计算机系统>一书中提出的概念. 基于成本.效率的考量,计算机存储器被设计成多级金字塔结构,塔顶是速度最快.成本最高的 CPU 内部的寄存器(一般几 KB)与高速缓存,塔底是成本最低.速度最慢的广域网云存储(如百度云免费 2T ) 存储器山的指导意义在于揭示了良好设计程序的必要条件是需要有优秀的局部性: 时间局部性:相同时间内,访问同一地址次数越多,则时间局部性表现越佳; 空间局部性:下一次访问的存储器地址与上一次的访

python协程用法实例分析

  本文实例讲述了python协程用法.分享给大家供大家参考.具体如下: 把函数编写为一个任务,从而能处理发送给他的一系列输入,这种函数称为协程 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def print_matchs(matchtext): print "looking for",matchtext while True: line = (yield) #用 yield语句并以表达式(yield)的形式创建协程 if matchtext in

Python协程:概念及其用法

真正有知识的人的成长过程,就像麦穗的成长过程:麦穗空的时候,麦子长得很快,麦穗骄傲地高高昂起,但是,麦穗成熟饱满时,它们开始谦虚,垂下麦芒. --蒙田<蒙田随笔全集> 上篇<Python 多线程鸡年不鸡肋>论述了关于python多线程是否是鸡肋的问题,得到了一些网友的认可,当然也有一些不同意见,表示协程比多线程不知强多少,在协程面前多线程算是鸡肋.好吧,对此我也表示赞同,然而上篇我论述的观点不在于多线程与协程的比较,而是在于IO密集型程序中,多线程尚有用武之地. 对于协程,我表示其

一个使用 asyncio 协程的网络爬虫(二)

协程 还记得我们对你许下的承诺么?我们可以写出这样的异步代码,它既有回调方式的高效,也有多线程代码的简洁.这个结合是同过一种称为协程coroutine的模式来实现的.使用 Python3.4 标准库 asyncio 和一个叫"aiohttp"的包,在协程中获取一个网页是非常直接的( @asyncio.coroutine 修饰符并非魔法.事实上,如果它修饰的是一个生成器函数,并且没有设置 PYTHONASYNCIODEBUG 环境变量的话,这个修饰符基本上没啥用.它只是为了框架的其它部分

一个使用 asyncio 协程的网络爬虫(三)

使用协程 我们将从描述爬虫如何工作开始.现在是时候用 asynio 去实现它了. 我们的爬虫从获取第一个网页开始,解析出链接并把它们加到队列中.此后它开始傲游整个网站,并发地获取网页.但是由于客户端和服务端的负载限制,我们希望有一个最大数目的运行的 worker,不能再多.任何时候一个 worker 完成一个网页的获取,它应该立即从队列中取出下一个链接.我们会遇到没有那么多事干的时候,所以一些 worker 必须能够暂停.一旦又有 worker 获取一个有很多链接的网页,队列会突增,暂停的 wo

Python3.5 协程原理

本文讲的是Python3.5 协程原理, 作为 Python 核心开发者之一,让我很想了解这门语言是如何运作的.我发现总有一些阴暗的角落我对其中错综复杂的细节不是很清楚,但是为了能够有助于 Python 的一些问题和其整体设计,我觉得我应该试着去理解 Python 的核心语法和内部运作机制. 但是直到最近我才理解 Python 3.5 中 async/await 的原理.我知道 Python 3.3 中的 yield from 和 Python 3.4 中的asyncio 组合得来这一新语法.但

PHP实现协程

在服务器编程当中,为了实现异步,经常性的需要回调函数,例如以下这段代码 function send($value) { $data = process($value); onReceive($data); } function onReceive($recv_value) { var_dump($recv_value); } function process($value) { return $value+1; } $send_value = 1; send($send_value); 实现的东

PHP协程实现过程详解

多进程/线程 最早的服务器端程序都是通过多进程.多线程来解决并发IO的问题.进程模型出现的最早,从Unix 系统诞生就开始有了进程的概念.最早的服务器端程序一般都是 Accept 一个客户端连接就创建一个进程,然后子进程进入循环同步阻塞地与客户端连接进行交互,收发处理数据. 多线程模式出现要晚一些,线程与进程相比更轻量,而且线程之间共享内存堆栈,所以不同的线程之间交互非常容易实现.比如实现一个聊天室,客户端连接之间可以交互,聊天室中的玩家可以任意的其他人发消息.用多线程模式实现非常简单,线程中可