asyncio 自动跳出长时间堵塞的 task

https://www.cnblogs.com/ywhyme/p/10660411.html 的升级版
可以知道当前是卡在哪一个 task 甚至是多少行

import asyncio
import os
import queue
import signal
import time
import threading
import logging

# logging.basicConfig(level=logging.DEBUG, format="%(asctime)s  %(filename)s : %(levelname)s  %(message)s", )
logging.basicConfig(level=logging.DEBUG)

# 魔改部分
# 魔改原因, 在 loop.debug 为 True 的时候才会给 loop 设置 _current_handle
from asyncio import events


class Handle(events.Handle):
    def _run(self):
        self._loop._current_handle = self
        super()._run()


events.Handle = Handle
# 魔改结束


async def factorial(name, number):
    f = 1
    for i in range(2, number + 1):
        print(f"Task {name}: Compute factorial({i})...")
        await asyncio.sleep(1)
        f *= i
    print(f"Task {name}: factorial({number}) = {f}")


async def test():
    for i in range(100):
        print("sleep--", i)
        time.sleep(1)


def handler(signum, frame):
    print('Signal handler called with signal', signum)
    raise Exception("Kill the task")


signal.signal(signal.SIGTERM, handler)


async def main():
    await asyncio.gather(
        test(),
        factorial("A", 2),
        factorial("B", 3),
        factorial("C", 4),
        return_exceptions=True
    )


def check(co_name, threshold: int = 60) -> bool:
    """连续的记录超过阈值"""
    i = 0
    for item in q:
        if item == co_name:
            i += 1
        else:
            break
    if i >= threshold:
        return True
    else:
        return False


def asyncio_monitor(loop, step: int = 1):
    while not stop:
        if hasattr(loop._current_handle, "_callback"):
            callback = loop._current_handle._callback
            task = getattr(callback, "__self__")  # Task

            co_name = task._coro.cr_code.co_name  # task._coro.cr_code.co_name # coro name

            if check(co_name, 10):
                # 长时间堵塞, 抛出异常让 task 结束
                if pid != None:
                    os.system(f"kill -{signal.SIGTERM} {pid}")

            if task._state == "PENDING":
                q.appendleft(co_name)

            # info 为一个的回显字符串

            info = str(getattr(callback, "__self__", callback))
            print(info)
            #
            # coro = task._coro.co_name # coro name
            # task._state  # futures/_base.py:25
            #

        time.sleep(step)


def run_asyncio(loop):
    global stop
    # loop.set_debug(True)
    loop.run_until_complete(main())
    stop = True


if __name__ == '__main__':
    pid = os.getpid()
    print(pid)
    stop = False

    q = queue.deque(maxlen=100)

    loop = asyncio.get_event_loop()

    t1 = threading.Thread(target=asyncio_monitor, args=(loop,))
    t1.start()

    run_asyncio(loop)

转载于:https://www.cnblogs.com/ywhyme/p/10731771.html

### Python `asyncio` 实现不等待的异步调用 在 Python 的 `asyncio` 编程模型中,可以通过创建任务而不立即等待其完成来实现“不等待”的异步操作。具体来说,可以使用 `asyncio.create_task()` 方法将协程封装成一个任务并将其加入事件循环,而无需阻塞主线程等待该任务的结果。 以下是基于所提供的引用内容以及扩展的知识构建的一个示例: ```python import asyncio async def long_running_task(task_id): """模拟一个长时间运行的任务""" print(f"Task {task_id} started.") await asyncio.sleep(5) # 模拟耗时操作 print(f"Task {task_id} completed.") async def main(): print("Main function started.") # 创建多个任务但不等待它们完成 task1 = asyncio.create_task(long_running_task(1)) # 不等待任务1 task2 = asyncio.create_task(long_running_task(2)) # 不等待任务2 # 主线程继续执行其他逻辑 print("Main function continues without waiting for tasks to complete...") await asyncio.sleep(1) # 模拟一些其他工作 print("Other work done in the meantime.") # 如果需要最终获取结果或确保任务完成,可以选择性地等待 await asyncio.gather(task1, task2) # 启动主函数 asyncio.run(main()) ``` 在这个例子中,通过 `asyncio.create_task()` 将两个长期运行的任务提交给事件循环,但并未立刻等待这些任务结束。这使得程序可以在启动任务后继续执行其他逻辑[^3]。 如果希望完全忽略某些任务的结果甚至不需要关心它们是否已完成,则可以直接创建任务而不对其进行任何后续处理。不过需要注意的是,在这种情况下应确保任务不会抛出未捕获异常,否则可能导致警告信息被记录下来。 另外值得注意的一点是关于超时控制方面,当存在可能无限期挂起或者非常长周期才能返回响应的情况时,可以考虑利用 `asyncio.timeout()` 来设置合理的最大等待时间以防止资源浪费或死锁现象发生[^2]。 #### 关于取消任务 有时还需要能够主动终止不再需要的任务。为此可以借助 `Task.cancel()` 方法手动触发取消信号,并配合 try-except 结构捕捉由此产生的 `CancelledError` 异常从而优雅退出[^4]。 --- ### 问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值