再探协程和线程的比较——实例

2022-05-06  本文已影响0人  东方胖

之前对比过在 IO 发生了上下文切换时线程和协程的对比

本文继续探讨几个细节。
例子来自 《流畅的Python》一书

例子是在控制台刷出一个旋转的 ‘/’ 字符
线程的写法:

import itertools
import threading
import time
import sys


class Signal:
    go = True


def spin(msg, signal):
    write = sys.stdout.write
    flush = sys.stdout.flush
    for char in itertools.cycle("|/-\\"):
        status = char + ' ' + msg
        write(status)
        flush()
        write('\x08' * len(status))
        time.sleep(0.1)
        if not signal.go:
            break
        write(' ' * len(status) + '\x08' * len(status))


def slow_function():
    time.sleep(3)
    return 42


def supervisor():
    signal = Signal()
    spinner = threading.Thread(target=spin, args=('doing', signal))
    print("spin object:", spinner)
    spinner.start()
    result = slow_function()
    signal.go = False
    spinner.join()
    return result


def main():
    result = supervisor()
    print("Answer:", result)


if __name__ == '__main__':
    main()

这个框架可以用来处理那些需要让用户等待,但耗时任务时不那么无聊的慢任务,已经我把它改成一个装饰器,放到我的代码库里

我们说,IO任务,尽量使用异步协程来处理
它比线程的好处有很多,
第一个是,代码不需要另辟蹊径取一个线程,同时要考虑一些锁,临界区,以及线程管理的问题。当然协程也是有一些代价的,它在低版本 Python 支持的不是那么好,同时,像 yield, yield from 装饰器 ayncio.coroutine 经历了一些变化,到现在 Python3.10 的 async await语法,对用户来说,有一些困惑。

到 Python3.8 后,基本上,我们可以用 async await 来写协程。

上面的例子用协程的写法如下:

import asyncio
import sys
import itertools


async def spin(msg):
    write = sys.stdout.write
    flush = sys.stdout.flush
    for char in itertools.cycle('|/-\\'):
        status = char + ' ' + msg
        write(status)
        flush()
        write('\x08' * len(status))
        try:
            await asyncio.sleep(0.05)
        except asyncio.CancelledError:
            break

    write(' ' * len(status) + '\x08' * len(status))


async def slow_function():
    await asyncio.sleep(3)
    return 42


async def supervisor():
    spinner = asyncio.create_task(spin('thiking'))
    print('spinner object: ', spinner)
    result = await slow_function()
    spinner.cancel()
    return result


def main():
    loop = asyncio.get_event_loop()
    result = loop.run_until_complete(supervisor())
    loop.close()
    print("Answer:", result)

if __name__ == '__main__':
    main()
上一篇下一篇

猜你喜欢

热点阅读