python线程与协程

协程与线程有什么区别,我觉得主要区别是:线程是操作系统来控制什么时候切换运行程序,而协程是程序员自己决定什么时候交出运行权。

一、线程

线程在任何编程语言中,都算是比较难的内容,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。

  • wait(timeout=None):阻塞线程直到内部标志为True,或者发生超时事件。如果调用时内部标志就是True,那么不会被阻塞,否则将被阻塞。timeout为浮点类型的秒数。

二、协程

#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()

运行效果与上面的线程相似:

协程涉及到的新概念很多,在此讲几个最重要的:

  1. event_loop 事件循环:程序开启一个无限的循环,程序员会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。
  2. coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环(loop),由事件循环调用run_until_complete()运行。
  3. task 任务:可以理解为单个coroutine,经过ensure_future()方法处理而形成,而众多task所组成的集合经过asyncio.gather处理而形成一个future。。
  4. future: 代表将来执行或没有执行的任务。task是future的子类,future就是存放着众多task或future的容器。
  5. async/await 关键字:协程方法需要用@asyncio.coroutine注解、yield from,python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口,这两个关键字可以让代码看起来更简洁,表达力更强。
  6. run_until_complete(future),既可以接收 Future 对象,也可以是 coroutine 对象,如果是coroutine,则通过ensure_future()转化为future。
  7. asyncio.gather 可以将多个 Future 和 coroutine 封装成一个 Future,因为run_until_complete()只授受一个参数,所以当涉及多个协程调用时,gather()很有用。
  8. 可以通过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()

上面代码采用了新的关键字和函数,语义更加明确。

你可能感兴趣的:(python,python)