当前位置:   article > 正文

Python - 协程基本使用详解【demo】_phthon协程执行图

phthon协程执行图

一. 前言

协程(Coroutine)是一种轻量级的线程,也被称为用户级线程或绿色线程。它是一种用户态的上下文切换方式,比内核态的线程切换更为轻量级,能够高效的支持大量并发操作。

2. 使用协程的好处

Python 中的协程是通过 asyncio 模块实现的,使用协程可以带来以下好处:

  1. 轻量级并发操作:协程的运行过程中不需要进行线程切换和上下文切换,避免了这些操作的开销,因此比传统的线程更加轻量级,可以支持更多并发操作。
  2. 提高程序性能:协程避免了线程上下文切换的开销,可以大大提高程序的执行效率。并且在 Python 中,协程的调度是由事件循环(Event
    Loop)来完成的,事件循环采用的是单线程方式,能够充分利用 CPU 资源,进一步提高了程序的性能。
  3. 简化异步编程:协程使得异步编程更加简单,在编写异步程序时可以避免回调地狱(Callback
    Hell)的问题,提高程序的可读性和可维护性。而且通过 Python 的 async/await
    语法,可以使用同步的方式编写异步代码,使得代码更加易于理解和调试。
  4. 更加灵活的控制流程:协程可以方便地进行挂起和恢复操作,因此可以很灵活地控制流程。例如可以在协程中使用条件判断、循环等语句,进行更加复杂的流程控制操作。

总之,协程是一种高效、轻量级的并发编程方式,能够提高程序的性能,简化异步编程,使得控制流程更加灵活,是 Python 中重要的并发编程工具之一。

三. 代码示例

1. 基本使用

import asyncio

async def task1():
    await asyncio.sleep(1)
    print('Task 1 done')

async def task2():
    await asyncio.sleep(2)
    print('Task 2 done')

async def main():
    print('Starting tasks')
    # 并发执行 task1 和 task2 任务
    await asyncio.gather(task1(), task2())
    '''列表传参启动'''
    # tasks = [task1(), task2()]  # 创建协程任务列表
    # await asyncio.gather(*tasks)  # 同时运行所有协程任务
    print('All tasks done')

asyncio.run(main())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

也可以使用列表传参的方式将任务传进去启动

tasks = [task1(), task2()]  # 创建协程任务列表
await asyncio.gather(*tasks)  # 同时运行所有协程任务
  • 1
  • 2

运行结果
在这里插入图片描述

2. 进阶使用:异步任务循环任务

方法一:

import asyncio
import random

async def producer(queue):
    while True:
        value = random.randint(0, 10)
        print(f"Produced: {value}")
        await queue.put(value)
        await asyncio.sleep(random.random())

async def consumer(queue):
    while True:
        value = await queue.get()
        print(f"Consumed: {value}")
        await asyncio.sleep(random.random())

async def main():
    queue = asyncio.Queue()
    task_producer = asyncio.create_task(producer(queue))
    task_consumer = asyncio.create_task(consumer(queue))
    await asyncio.gather(task_producer, task_consumer)

asyncio.run(main())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

运行结果
在这里插入图片描述

在上面的代码中,我们定义了一个 producer 函数和一个 consumer 函数,它们都接受一个 asyncio.Queue 对象作为输入,并在其中实现协程的逻辑。其中 producer 函数会在队列中不断生成随机数,并将其放入队列中;consumer 函数则会不断从队列中取出随机数并进行消费。

在 main 函数中,我们首先创建了一个 asyncio.Queue 对象用于协程之间的通信,然后使用 asyncio.create_task 函数创建了两个任务,分别是生产者任务和消费者任务。最后,我们使用 asyncio.gather 函数来运行这两个任务,当其中任何一个任务完成时,main 函数也会结束。

需要注意的是,当我们使用协程时,需要使用 await 关键字来挂起协程的执行,等待其他协程的执行或者等待 I/O 操作完成。同时,在协程中不应该使用阻塞式的操作,比如 time.sleep(),而应该使用异步 I/O 操作,比如 asyncio.sleep()

方法二 & 方法三

import asyncio
import random


async def producer(queue):
    while True:
        value = random.randint(0, 10)
        print(f"Produced: {value}")
        await queue.put(value)
        await asyncio.sleep(random.random())

async def consumer(queue):
    while True:
        value = await queue.get()
        print(f"Consumed: {value}")
        await asyncio.sleep(random.random())


async def main():
    queue = asyncio.Queue()
    '''方法一'''
    # task_producer = asyncio.create_task(producer(queue))
    # task_consumer = asyncio.create_task(consumer(queue))
    # await asyncio.gather(task_producer, task_consumer)
    '''方法二:直接启动'''
    # await asyncio.wait([producer(queue), consumer(queue)])
    '''方法三'''
    task_producer = asyncio.ensure_future(producer(queue))
    task_consumer = asyncio.ensure_future(consumer(queue))
    await asyncio.wait([task_producer(queue), task_consumer(queue)])


asyncio.run(main())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

以上协程的几种启动方式的的区别在于它们等待任务完成的方式不同。

  • await asyncio.gather(task_producer, task_consumer)
    会并发地运行多个任务,并等待它们全部完成后才会返回结果。也就是说,这种方式只有在所有任务都执行完成后,才会进入下一步。
  • await asyncio.wait([producer(queue), consumer(queue)])则是将多个协程对象(通过列表进行传递)传递给 asyncio.wait()函数,这个函数也会等待多个协程同时完成。不同的是,asyncio.wait() 函数会返回两个集合(即 done 和pending),done 集合包含已经完成的任务,pending 集合包含还未完成的任务。因此可以通过遍历 done集合取得协程的结果。
  • asyncio.ensure_future() 可以将协程函数包装为一个 Future对象,它可以被添加到事件循环中进行异步执行。与直接调用协程函数不同,使用 asyncio.ensure_future() 会创建一个Future 对象并返回它,这个对象可以被传递到其他地方进行管理和操作。
  • 使用 asyncio.ensure_future()的主要优势是可以在多个协程之间共享对象,使得协程之间可以协同工作。同时,它还可以使协程函数更加可读性高,在协程之间传递数据时也更加方便。
  • 另一方面,直接调用协程函数并使用 await关键字来等待其完成也是完全可行的。在某些情况下,这种方法甚至可以更加简单和直观。但是,当需要同时执行多个协程或使用 asyncio库中提供的其他高级功能时,使用 asyncio.ensure_future() 可能会更方便。

总的来说

  • asyncio.gather() 更加简单易用,适合并发执行多个任务并等待它们全部完成的情况。
  • asyncio.wait() 则更加灵活,并且可以实时获取任务的执行结果。
  • asyncio.ensure_future()是一种更加通用的方法,适用于大多数协程编程的情况,但是在某些情况下,使用直接调用协程函数和 await 可能更加简单和有效。

3. 启动方式差异

注意,在 Python 3.7 以前,我们需要使用 asyncio.get_event_loop().run_until_complete() 函数来运行协程任务。但在 Python 3.7 及以后的版本中,我们可以使用更为简洁的 asyncio.run() 函数来运行协程任务。
示例代码如下:

import asyncio
import random


async def producer(queue):
    while True:
        value = random.randint(0, 10)
        print(f"Produced: {value}")
        await queue.put(value)
        await asyncio.sleep(random.random())

async def consumer(queue):
    while True:
        value = await queue.get()
        print(f"Consumed: {value}")
        await asyncio.sleep(random.random())


async def main():
    queue = asyncio.Queue()
    '''方法一'''
    # task_producer = asyncio.create_task(producer(queue))
    # task_consumer = asyncio.create_task(consumer(queue))
    # await asyncio.gather(task_producer, task_consumer)
    '''方法二:直接启动'''
    # await asyncio.wait([producer(queue), consumer(queue)])
    '''方法三'''
    task_producer = asyncio.ensure_future(producer(queue))
    task_consumer = asyncio.ensure_future(consumer(queue))
    await asyncio.wait([task_producer(queue), task_consumer(queue)])


'''协程启动的方式一'''
# asyncio.run(main())

'''协程启动的方式二'''
new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)
new_loop.run_until_complete(main())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

总之,协程是一种高效、轻量级的并发编程方式,能够提高程序的性能,简化异步编程,使得控制流程更加灵活,是 Python 中重要的并发编程工具之一。

以上就是关于Python - 协程基本使用的介绍,希望对你有所帮助,谢谢!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/102651
推荐阅读
相关标签
  

闽ICP备14008679号