如何修复'错误:asyncio:任务已被破坏,但它正在等待处理! Python中的错误

Posted

技术标签:

【中文标题】如何修复\'错误:asyncio:任务已被破坏,但它正在等待处理! Python中的错误【英文标题】:How to fix 'ERROR:asyncio:Task was destroyed but it is pending! error in Python如何修复'错误:asyncio:任务已被破坏,但它正在等待处理! Python中的错误 【发布时间】:2019-09-04 13:45:45 【问题描述】:

我有一个简单的 websocket 服务器,它应该将相机帧提供给电子应用程序。代码主要取自here。这个想法是,当我打开 websocket 时,python 将启动一个 while 循环,该循环不断发送将由 js“客户端”捕获的帧。但是,为了能够停止这个 while 循环,我需要在执行程序上运行这个方法,否则它会卡在其中。只要未设置事件并且在 websocket 的 on_close 方法上设置此事件,循环就应该运行。

我制作了一个单独的方法来模拟相机,在实际应用中,帧来自另一个进程。

问题是在运行程序一段时间后,我的日志被以下内容淹没:ERROR:asyncio:Task was destroy but it is pending!

我的假设是主 ioloop 和 executors 循环之间存在一些同步问题。我也尝试过在打开/关闭 websocket 时启动/停止发送帧的线程,但遇到了同样的问题......

这是我的代码:

ws_server.py

import cv2
import numpy
import asyncio

import traceback

from concurrent.futures import ThreadPoolExecutor

from tornado.websocket import WebSocketHandler
from tornado.concurrent import run_on_executor
from tornado.ioloop import IOLoop
from tornado.web import Application

from threading import Event


class StreamHandler(WebSocketHandler):

    executor = ThreadPoolExecutor()

    def check_origin(self, origin):
        return True

    def initialize(self):
        self.stop_event = Event()
        self.camera = self.frame_generator()

    def frame_generator(self):

        while not self.stop_event.is_set():
            frame = numpy.random.randint(0, 255, (800, 800))
            _, frame = cv2.imencode('.jpg', frame)
            frame = frame.tostring()

            yield frame

    def open(self):

        print("Connection opened.")
        self.send_frames()

    @run_on_executor
    def send_frames(self):
        asyncio.set_event_loop(asyncio.new_event_loop())
        while not self.stop_event.is_set():

            img = next(self.camera)

            if img is not None:
                try:
                    self.write_message(img, binary=True)
                except:
                    print(traceback.format_exc())

    def on_close(self):
        self.stop_event.set()
        print("Connection closed.")


def main():

    print("starting server.")
    app = Application([
        (r"/camera", StreamHandler),
    ])
    app.listen(8083)
    IOLoop.instance().start()


if __name__ == '__main__':
    main()

client.js

var img = document.getElementById("liveImg");
var arrayBuffer;

var ws = new WebSocket("ws://localhost:8083/camera");
ws.binaryType = 'arraybuffer';

ws.onopen = function()
    console.log("connection was established");
;
ws.onmessage = function(evt)
arrayBuffer = evt.data;
img.src = "data:image/jpeg;base64," + encode(new Uint8Array(arrayBuffer));
;

function encode (input) 
    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var output = "";
    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;

    while (i < input.length) 
        chr1 = input[i++];
        chr2 = i < input.length ? input[i++] : Number.NaN; // Not sure if the index
        chr3 = i < input.length ? input[i++] : Number.NaN; // checks are needed here

        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
        enc4 = chr3 & 63;

        if (isNaN(chr2)) 
            enc3 = enc4 = 64;
         else if (isNaN(chr3)) 
            enc4 = 64;
        
        output += keyStr.charAt(enc1) + keyStr.charAt(enc2) +
                  keyStr.charAt(enc3) + keyStr.charAt(enc4);
    
    return output;

index.html

<html>
 <head>
  <title>livecamera</title>
  <img id="liveImg"  ></canvas>
  <script type="text/javascript" src="./client.js"></script>
 </head>
</html>

下面是我的日志中的一个示例:

ERROR:asyncio:Task 已销毁,但它正在等待处理! 任务:.wrapper() 在 /home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102 运行 错误:asyncio:任务已被破坏,但它正在等待处理! 任务:.wrapper() 在 /home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102 运行 错误:asyncio:任务已被破坏,但它正在等待处理! 任务:.wrapper() 在 /home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102 运行 错误:asyncio:任务已被破坏,但它正在等待处理! 任务:.wrapper() 在 /home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102 运行 错误:asyncio:任务已被破坏,但它正在等待处理! 任务:.wrapper() 在 /home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102 运行 错误:asyncio:任务已被破坏,但它正在等待处理! 任务:.wrapper() 在 /home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102 运行 错误:asyncio:任务已被破坏,但它正在等待处理! 任务:.wrapper() 运行在 /home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102>>

【问题讨论】:

【参考方案1】:

不要在执行线程中创建新的事件循环。

write_message 必须从处理连接的同一事件循环线程中调用。这意味着如果您使用执行器,则必须在执行器和事件循环之间来回传递消息,以便您可以在执行器上执行阻塞任务并在事件循环上写入 websocket 消息。在执行程序中创建一个新的事件循环将绕过关于线程中没有事件循环的错误,但这是不正确的 - 这些警告的重点是您必须使用 same 事件循环,而不是创建一个新的。

在实践中,我建议尽可能多地在事件循环线程上做,并且只将特定的阻塞工作传递给执行器:

async def send_frames(self):
    while not self.stop_event.is_set():

        img = await IOLoop.current().run_in_executor(self.executor, next, self.camera)

        if img is not None:
            try:
                await self.write_message(img, binary=True)
            except:
                print(traceback.format_exc())

【讨论】:

嗨,Ben,感谢您抽出宝贵时间发布您的答案。我已经尝试过了,但我得到了RuntimeWarning: coroutine 'StreamHandler.send_frames' was never awaited self.send_frames()。另外,如果我让我的open() 方法异步并等待self.send_frames(),我会得到TypeError: object Future can't be used in 'await' expression... 抱歉,忘记了原生协程在等待协程时需要 run_in_executor 助手。我已经更新了答案。

以上是关于如何修复'错误:asyncio:任务已被破坏,但它正在等待处理! Python中的错误的主要内容,如果未能解决你的问题,请参考以下文章

ASYNCIO:[错误] 任务被破坏,但它处于挂起状态

Python3 asyncio“任务已被破坏,但处于待处理状态”,具有某些特定条件

asyncio 任务已被销毁,但它处于挂起状态

取消 asyncio 任务导致“任务被破坏但它处于挂起状态”

Python asyncio/discord.py - 循环退出,任务被破坏,但它处于待处理状态

请解释“任务已被破坏但未决!”取消任务后