基于Python3单进程+多线程(伪)+多协程的生产者-消费者模型示例代码

在这里插入图片描述
图片来源:FGC/Shutterstock


以下代码实现了在单进程下,主线程不断产生数据,并传入子线程中;由子线程中的事件循环异步地将数据放入队列中,再异步地消费掉,消费完后释放所有相关资源;另外每个资源包都配有异步倒计时器,一旦倒计时完成,强制释放资源

import asyncio
from threading import Thread
import time

def start_loop(loop):
    asyncio.set_event_loop(loop)
    loop.run_forever()

def create(timestamp, worker_pool, worker_loop):
    worker_pool[timestamp] = {}
    consumer_task = asyncio.run_coroutine_threadsafe(consumer(worker_pool, timestamp), worker_loop)
    timeout_task = asyncio.run_coroutine_threadsafe(timeout(worker_pool, timestamp), worker_loop)
    worker_pool[timestamp]['consumer'] = consumer_task
    worker_pool[timestamp]['timeout'] = timeout_task
    asyncio.run_coroutine_threadsafe(producer(worker_pool, timestamp), worker_loop)

async def producer(worker_pool, timestamp):
    await worker_pool[timestamp]['mq'].put(timestamp)

async def consumer(worker_pool, timestamp):
    worker_pool[timestamp]['mq'] = asyncio.Queue()
    while True:
        await worker_pool[timestamp]['mq'].get()
        worker_pool[timestamp]['timeout'].cancel()
        worker_pool.pop(timestamp)
        break

async def timeout(worker_pool, timestamp):
    await asyncio.sleep(10)
    worker_pool[timestamp]['consumer'].cancel()
    worker_pool.pop(timestamp)

if __name__ == "__main__":
    worker_loop = asyncio.new_event_loop()
    loop_thread = Thread(target=start_loop, args=(worker_loop,))
    loop_thread.daemon = True
    loop_thread.start()
    worker_pool = {}

    count = 0
    start = time.time()
    while True:
        if time.time() - start > 1:
            start = time.time()
            print(count)
            count = 0
        count += 1
        timestamp = str(time.time())
        create(timestamp, worker_pool, worker_loop)
        print(f'-------- {len(worker_pool)}', end='\r', flush=True)

之所以要使用多线程

  1. 是因为要将数据产生的过程与其他过程隔离,从而不会因为其他过程的阻塞而影响数据产生过程,而实现这种隔离可以用多线程,也可以用多进程,而不能用协程,因为协程是用户态的线程,其调度不受操作系统控制。
  2. 可以实现在事件循环中动态添加任务

所以主线程的任务包括:

  • 以sleep_time时间间隔循环产生时间戳数据timestamp
  • 以时间戳为键,在worker_pool中创建字典数据,资源包括消费者任务对象、倒计时任务对象、数据队列对象
  • 将数据传递给子线程的事件循环中,创建生产者任务、消费者任务、倒计时任务

子线程的任务包括:

  • 执行事件循环
  • 接受数据,执行生产者任务、消费者任务、倒计时任务
    • 生产者任务:异步方式向队列传入数据
    • 消费者任务:异步方式从队列取出数据、释放相关资源
    • 倒计时任务:异步方式倒计时,倒计时完成强制释放相关资源

下图可见即使每秒产生2000+条数据,也能消费得过来,没有产生大规模数据堆积
(由于Mac上的Docker无法做到像Linux Docker一样完全使用Linux内核,因而速度会慢一些,在Mac环境下测试每秒能产生5000+条)
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值