协程与线程有什么区别,我觉得主要区别是:线程是操作系统来控制什么时候切换运行程序,而协程是程序员自己决定什么时候交出运行权。
一、线程
线程在任何编程语言中,都算是比较难的内容,python也不例外。先看看《流畅的python》书中的例子:
#win7+python3.7
import threading
import itertools
import time
import sys
def spin(msg, done): # <2>
write, flush = sys.stdout.write, sys.stdout.flush
for char in itertools.cycle('|/-\\'): # 无限循环依次输出符号
status = char + ' ' + msg
write(status)
flush()
write('\x08' * len(status)) # 退格键,实现动态效果
if done.wait(.1): # 等待0.1秒,如果内部标志为true,退出。0.1在这里表示速度。
break
write(' ' * len(status) + '\x08' * len(status)) # \x08表示退格符,是实现动态效果的关键
def slow_function(): # <7>
# pretend waiting a long time for I/O
time.sleep(3) # <8>
return 42
def supervisor(): # <9>
done = threading.Event()#注意,采用的是threading,老的thread被废弃了
spinner = threading.Thread(target=spin,args=('thinking!', done))
print('spinner object:', spinner) # <10>
spinner.start() # <11>
result = slow_function() # <12>
done.set() # 将Event的内部状态标志设为true
spinner.join() # <14>
return result
def main():
result = supervisor() # <15>
print('Answer:', result)
if __name__ == '__main__':
main()
运行效果:
持续3秒后,返回结果:
(1)事件对象:threading.Event
一个事件对象管理一个内部标志,初始状态默认为False,set()方法可将它设置为True,clear()方法可将它设置为False,wait()方法将线程阻塞直到内部标志的值为True。
二、协程
#win7+python3.7
import asyncio
import itertools
import sys
@asyncio.coroutine # <1>
def spin(msg): # <2>
write, flush = sys.stdout.write, sys.stdout.flush
for char in itertools.cycle('|/-\\'):
status = char + ' ' + msg
write(status)
flush()
write('\x08' * len(status))
try:
yield from asyncio.sleep(.1) # <3>
except asyncio.CancelledError: # 下面的spinner.cancel()方法会抛出CancelledError异常
break
write(' ' * len(status) + '\x08' * len(status))
@asyncio.coroutine
def slow_function(): # <5>
# pretend waiting a long time for I/O
yield from asyncio.sleep(3) # <6>
return 42
@asyncio.coroutine
def supervisor(): # 监管员方法,因为调用协程方法的必须是协程或者loop,这个方法起一个程序流程的总控制作用
spinner = asyncio.ensure_future(spin('thinking!')) # 把spin协程包装成Task
print('spinner object:', spinner) # <9>
result = yield from slow_function() # <10>
spinner.cancel() # spinner任务被删除,抛出CancelledError异常
return result
def main():
loop = asyncio.get_event_loop() # <12>
result = loop.run_until_complete(supervisor()) # <13>
loop.close()
print('Answer:', result)
if __name__ == '__main__':
main()
运行效果与上面的线程相似:
协程涉及到的新概念很多,在此讲几个最重要的:
- event_loop 事件循环:程序开启一个无限的循环,程序员会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。
- coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环(loop),由事件循环调用run_until_complete()运行。
- task 任务:可以理解为单个coroutine,经过ensure_future()方法处理而形成,而众多task所组成的集合经过asyncio.gather处理而形成一个future。。
- future: 代表将来执行或没有执行的任务。task是future的子类,future就是存放着众多task或future的容器。
- async/await 关键字:协程方法需要用@asyncio.coroutine注解、yield from,python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口,这两个关键字可以让代码看起来更简洁,表达力更强。
- run_until_complete(future),既可以接收 Future 对象,也可以是 coroutine 对象,如果是coroutine,则通过ensure_future()转化为future。
- asyncio.gather 可以将多个 Future 和 coroutine 封装成一个 Future,因为run_until_complete()只授受一个参数,所以当涉及多个协程调用时,gather()很有用。
- 可以通过loop.create_task(coroutine)创建task,也可以通过 asyncio.ensure_future(coroutine)创建task,两个方法的作用一样,但是create_task是3.4.2才有的,ensure_future()可以实用于以前的版本,但是ensure_future()在老版本的名字叫asyncio.async()。python3.7可以用asyncio.create_task()替换,这个方法比ensure_future更容易理解。看起来有点混乱,但任何东西发展都有一个过程,没办法。
协程第二版代码:
# -*- coding: UTF-8 -*-
#win7+python3.7
import asyncio
import itertools
import sys
async def spin(msg): # <2>
write, flush = sys.stdout.write, sys.stdout.flush
for char in itertools.cycle('|/-\\'):
status = char + ' ' + msg
write(status)
flush()
write('\x08' * len(status))
try:
await asyncio.sleep(.1) # <3>
except asyncio.CancelledError: # 下面的spinner.cancel()方法会抛出CancelledError异常
break
write(' ' * len(status) + '\x08' * len(status))
async def slow_function(): # <5>
# pretend waiting a long time for I/O
await asyncio.sleep(3) # <6>
return 42
async def supervisor(): # 监管员方法,因为调用协程方法的必须是协程或者loop,这个方法起一个程序流程的总控制作用
spinner = asyncio.create_task(spin('thinking!')) # 把spin协程包装成Task
print('spinner object:', spinner) # <9>
result = await slow_function() # <10>
spinner.cancel() # spinner任务被删除,抛出CancelledError异常
return result
def main():
loop = asyncio.get_event_loop() # <12>
result = loop.run_until_complete(supervisor()) # <13>
loop.close()
print('Answer:', result)
if __name__ == '__main__':
main()
上面代码采用了新的关键字和函数,语义更加明确。