python asyncio协程动态添加任务、协程池

asyncio 协程介绍:

  • 动态添加任务:
    • 方案是创建一个线程,使事件循环在线程内永久运行
    • 设置守护进程,随着主进程一起关闭
  • 自动停止任务
  • 阻塞任务完成
  • 协程池
    • 队列自带阻塞机制,当队列满了后会阻塞,因此可以取代 asyncio.Semaphore()

注意

  • 通过项目实战发现这种方案依旧有限制,并不是我所想要的那种协程池;
  • 虽然实现了并发数限制,但是任务并不是实施加入任务队列的,而是一直堵塞在maxsize值,一旦协程池将下来再行添加任务;
  • 而我想要的协程应该是任务直接加入协程池,只不过每次并发限制在maxsize,较少因队列产生的损耗;
  • 下一篇将解决这个问题。

demo

import asyncio
import aiohttp
import time
import nest_asyncio
import queue
from threading import Thread


class AsyncPool(object):
    """
    1. 支持动态添加任务
    2. 支持自动停止事件循环
    3. 支持最大协程数
    """

    def __init__(self, loop=None, maxsize=0):
        """
        初始化
        :param loop:
        :param maxsize: 默认0,不限制队列
        """
        # 在jupyter需要这个,不然asyncio运行出错
        nest_asyncio.apply()

        # 获取一个事件循环
        if not loop:
            self.loop = asyncio.new_event_loop()

        # 队列,先进先出,根据队列是否为空判断,退出协程
        self.q = queue.Queue(maxsize)

        self.loop_thread = None
        if self.loop:
            self.start_thread_loop()

    def add(self, item=1):
        """
        添加任务
        :param item:
        :return:
        """
        self.q.put(item)

    def done(self, fn):
        """
        任务完成
        回调函数
        :param fn:
        :return:
        """
        if fn:
            pass
        self.q.get()
        self.q.task_done()

    def wait(self):
        """
        等待任务执行完毕
        :return:
        """
        self.q.join()

    @staticmethod
    def _start_thread_loop(loop):
        """
        运行事件循环
        :param loop: loop以参数的形式传递进来运行
        :return:
        """
        # 将当前上下文的事件循环设置为循环。
        asyncio.set_event_loop(loop)
        # 开始事件循环
        loop.run_forever()

    def start_thread_loop(self):
        """
        运行事件循环
        :return:
        """
        self.loop_thread = Thread(target=self._start_thread_loop, args=(self.loop,))
        # 设置守护进程
        self.loop_thread.setDaemon(True)
        # 运行线程,同时协程事件循环也会运行
        self.loop_thread.start()

    def stop_thread_loop(self, loop_time=1):
        """
        队列为空,则关闭线程
        :param loop_time:
        :return:
        """

        async def _close_thread_loop():
            """
            关闭线程
            :return:
            """
            while True:
                if self.q.empty():
                    self.loop.stop()
                    break
                await asyncio.sleep(loop_time)

        # 等待关闭线程
        asyncio.run_coroutine_threadsafe(_close_thread_loop(), self.loop)

    def submit(self, func, callback=None):
        """
        提交任务到事件循环
        :param func: 异步函数对象
        :param callback: 回调函数
        :return:
        """
        # 将协程注册一个到运行在线程中的循环,thread_loop 会获得一个环任务
        # 注意:run_coroutine_threadsafe 这个方法只能用在运行在线程中的循环事件使用
        future = asyncio.run_coroutine_threadsafe(func, self.loop)

        # 回调函数封装
        def callback_done(_future):
            try:
                if callback:
                    callback(_future)
            finally:
                self.done(_future)

        # 添加回调函数
        future.add_done_callback(callback_done)

    def release(self, loop_time=1):
        """
        释放线程
        :param loop_time:
        :return:
        """
        self.stop_thread_loop(loop_time)

    def running(self):
        """
        获取当前线程数
        :return:
        """
        return self.q.qsize()


async def thread_example(i):
    url = "http://127.0.0.1:8080/app04/async4?num={}".format(i)
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as res:
            # print(res.status)
            # print(res.content)
            return await res.text()


def my_callback(future):
    result = future.result()
    print('返回值: ', result)


def main():
    # 任务组, 最大协程数
    pool = AsyncPool(maxsize=10000)

    # 插入任务任务
    for i in range(100000):
        pool.add()
        pool.submit(thread_example(i), my_callback)

    # 停止事件循环
    pool.release()

    # 等待
    pool.wait()

    print("等待子线程结束...")


if __name__ == '__main__':
    start_time = time.time()
    main()
    end_time = time.time()
    print("run time: ", end_time - start_time)
Python asyncio 是一个用于编写高性能并发代码的模块,特别适合处理I/O密集型任务,如网络编程。使用 asyncio 和 sockets 可以创建高效的异步客户端,它可以同时处理多个连接请求,而不会阻塞主线程。 以下是一个简单的 asyncio Socket 客户端示例,它使用 `asyncio` 的 `create_task`、`connect()` 函数以及 `StreamReader` 和 `StreamWriter` 来与服务器进行异步通信: ```python import asyncio import aiohttp import socket async def async_client(message, host='localhost', port=8000): reader, writer = await asyncio.open_connection(host, port) print(f"Connected to server: {host}:{port}") # Write message to the server writer.write(message.encode()) await writer.drain() # Ensure the message is sent before reading # Read response from the server response = await reader.readuntil(b'\n') print(f"Received from server: {response.decode()}") # Close the connection writer.close() await writer.wait_closed() async def main(): message = "Hello, Server!" await async_client(message) if __name__ == "__main__": asyncio.run(main()) ``` 在这个例子中,`async_client` 函数创建了一个异步连接,发送消息,接收响应,并关闭连接。`main` 函数启动了客户端的异步操作。 相关问题: 1. 在异步客户端中,`await open_connection()` 返回的是什么? 2. `reader.readuntil()` 函数的作用是什么? 3. 如何确保在读取服务器响应之前,消息已经完全发送到服务器? 4. 异步客户端如何处理可能出现的连接错误?
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值