当前位置: 首页 > 图灵资讯 > 行业资讯> Python中的协程是什么

Python中的协程是什么

发布时间:2025-11-02 16:20:32

协程

python 在GIL下,只有一个线程可以在同一时间运行,所以对于CPU计算密集的程序,线程之间的切换成本被拖累,而以I/O为瓶颈的程序是协程所擅长的:

Python中的协程经历了很长一段时间的发展。它可能经历了以下三个阶段:

1.原始生成器变形yield/send;

2.引入@asyncio.coroutine和yield from;

3.在最近的Python3.5版本中引入async/await关键词。

(1)从yield开始

先看一个普通的计算斐波那契续列的代码

deffibs(n):
res=[0]*n
index=0
a=0
b=1
whileindex<n:
res[index]=b
a,b=b,a+b
index+=1
returnres


forfib_resinfibs(20):
print(fib_res)

如果我们只需要获得斐波那契序列的N位,或者只是想产生斐波那契序列,那么上述传统方法将消耗更多的内存。

这时,yield就派上用场了。

deffib(n):
index=0
a=0
b=1
whileindex<n:
yieldb
a,b=b,a+b
index+=1

forfib_resinfib(20):
print(fib_res)

当一个函数包含yield语句时,python会自动将其识别为生成器。此时,fib(20)并没有真正调用函数体,而是通过函数体生成了生成器对象的例子。

yield可以在这里保留fib函数的计算场景,暂停fib计算并返回b。当fib放入for..in周期时,每个周期都会调用next(fib(20)唤醒生成器,执行到下一个yield语句,直到抛出stopiteration异常。这种异常会被for循环捕获,导致跳出循环。

(2) Send来了

从上面的程序可以看出,只有数据通过yield从fib(20)流向外部for循环;如果数据可以发送到fib(20),那么协程就可以在python中实现。

相关推荐:Python视频教程

因此,Python中的生成器具有send函数,yield表达式也具有返回值。

我们用这个特性来模拟慢速斐波那契数列的计算:

importtime
importrandom

defstupid_fib(n):
index=0
a=0
b=1
whileindex<n:
sleep_cnt=yieldb
print('letmethink{0}secs'.format(sleep_cnt))
time.sleep(sleep_cnt)
a,b=b,a+b
index+=1


print('-'*10+'testyieldsend'+'-'*10)
N=20
sfib=stupid_fib(N)
fib_res=next(sfib)
whileTrue:
print(fib_res)
try:
fib_res=sfib.send(random.uniform(0,0.5))
exceptStopIteration:
break

python 并发编程

Python 在2时代,Twisted主要用于高性能网络编程、Tornado和Gevent这三个库,但它们的异步代码既不兼容也不能移植。

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

asyncio的编程模型是一个新闻循环。我们直接从asyncio模块中获得Eventloop的引用,然后将要执行的协程扔进Eventlop中执行,实现异步IO。

Python在3.4中引入了协程的概念,但这仍然是基于生成器对象。

Python 3.5添加了async和await两个关键字,分别用于替换asyncio.coroutine和yield from。

python3.5.确定了协程的语法。下面将简要介绍asyncio的使用情况。不仅是asyncio实现了协程,tornado和gevent也实现了类似的功能。

(1)协程定义

Hellolloo实现asyncio world代码如下:

importasyncio

@asyncio.coroutine
defhello():
print("Helloworld!")
#异步调用asyncio.sleep(1):
r=yieldfromasyncio.sleep(1)
print("Helloagain!")

#Eventlooop获得Eventlop:
loop=asyncio.get_event_loop()
#执行coroutine执行coroutine
loop.run_until_complete(hello())
loop.close()

@asyncio.coroutine将generator标记为coroutine类型,然后我们将coroutine扔进eventlop中执行。 hello()首先打印Hello world!,然后,yield from语法可以让我们轻松地调用另一个generator。因为asyncior.sleep()也是coroutine,所以线程不会等asyncioio.sleep(),但直接中断并执行下一个消息循环。当asyncio.sleep()返回时,线程可以从yield返回 from获得返回值(这里是None),然后执行下一行语句。

asyncio.sleep(1)它被认为是一个需要1秒钟的IO操作。在此期间,主线程没有等待,而是执行EventLoop中可执行的其他coroutine,因此可以实现并发执行。

我们试着用Task包装两个coroutine:

importthreading
importasyncio

@asyncio.coroutine
defhello():
print('Helloworld!(%s)'%threading.currentThread())
yieldfromasyncio.sleep(1)
print('Helloagain!(%s)'%threading.currentThread())

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

观察执行过程:

Helloworld!(<_MainThread(MainThread,started140735193772>)
Helloworld!(<_MainThread(MainThread,started140735193772>)
(暂停约1秒)
Helloagain!(<_MainThread(MainThread,started140735193772>)
Helloagain!(<_MainThread(MainThread,started140735193772>)

从目前打印的线程名称可以看出,两个coroutine是同一线程并发执行的。

如果把asyncioio放在asy的话.sleep()用真实的IO操作代替,多个coroutine可以通过一个线程并发执行。

asyncio案例实战实战

我们使用asyncio的异步网络连接来获取sina、sohu和163网站首页:

async_wget.py

importasyncio

@asyncio.coroutine
defwget(host):
print('wget%s...'%host)
connect=asyncio.open_connection(host,80)
reader,writer=yieldfromconnect
header='GET/HTTP/1.0\r\nHost:%s\r\n\r\n'%host
writer.write(header.encode('utf-8'))
yieldfromwriter.drain()
whileTrue:
line=yieldfromreader.readline()
ifline==b'\r\n':
break
print('%sheader>%s'%(host,line.decode('utf-8').rstrip()))
#Ignorethebody,closethesocket
writer.close()

loop=asyncio.get_event_loop()
tasks=[wget(host)forhostin['www.sina.com.cn','www.sohu.com','www.163.com']]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

结果信息如下:

wgetwww.sohu.com...
wgetwww.sina.com.cn...
wgetwww.163.com...
(等待一段时间)
(打印sohuheader)
www.sohu.comheader>HTTP/1.1200OK
www.sohu.comheader>Content-Type:text/html
...
(打印sinaheader)
www.sina.com.cnheader>HTTP/1.1200OK
www.sina.com.cnheader>Date:Wed,20May201504:56:33GMT
...
(打印163header)
www.163.comheader>HTTP/1.0302Movedtemprarily
www.163.comheader>Server:Cdncacheserververververv2.0
...

可以看出,通过coroutine并发完成三个连接。

小结

asyncio提供完善的异步IO支持;

异步操作需要通过yield在coroutine中进行异步操作 完成from;

多个coroutine可以封装成一组task,然后并发执行。

相关文章

Python中的协程是什么

Python中的协程是什么

2025-11-02
Python中fock()函数如何使用

Python中fock()函数如何使用

2025-11-02
Python lambda表达式及用法

Python lambda表达式及用法

2025-11-02
Python中的threading模块是什么

Python中的threading模块是什么

2025-11-02
Python中Thread子类如何封装

Python中Thread子类如何封装

2025-10-31
Python中的并行和并发是什么

Python中的并行和并发是什么

2025-10-31