python - 如何修复'错误:asyncio:任务已被破坏,但它正在等待处理!Python中的错误
问题描述
我有一个简单的 websocket 服务器,它应该将相机帧提供给电子应用程序。代码大多取自这里。这个想法是,当我打开 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()
客户端.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;
}
索引.html
<html>
<head>
<title>livecamera</title>
<img id="liveImg" width="480" height="360"></canvas>
<script type="text/javascript" src="./client.js"></script>
</head>
</html>
这是我的日志中最终结果的示例:
错误:asyncio:任务已被破坏,但它正在等待处理!任务:.wrapper() 在/home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102>> 错误:asyncio:Task 被破坏但它正在等待中!任务:.wrapper() 在/home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102>> 错误:asyncio:Task 被破坏但它正在等待中!任务:.wrapper() 在/home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102>> 错误:asyncio:Task 被破坏但它正在等待中!任务:.wrapper() 在/home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102>> 错误:asyncio:Task 被破坏但它正在等待中!任务: 。wrapper() 在 /home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102>> 错误:asyncio:Task 被破坏但它正在等待中运行!任务:.wrapper() 在/home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102>> 错误:asyncio:Task 被破坏但它正在等待中!任务:.wrapper() 在 /home/vladinny/Projects/photo-booth-cv/venv/lib/python3.6/site-packages/tornado/websocket.py:1102 运行
解决方案
不要在执行线程中创建新的事件循环。
write_message
必须从处理连接的同一事件循环线程中调用。这意味着如果您使用执行器,则必须在执行器和事件循环之间来回传递消息,以便您可以在执行器上执行阻塞任务并在事件循环上写入 websocket 消息。在执行程序中创建一个新的事件循环将绕过关于线程中没有事件循环的错误,但这是不正确的 - 这些警告的重点是您必须使用相同的事件循环,而不是创建一个新的。
在实践中,我建议尽可能多地在事件循环线程上做,并且只将特定的阻塞工作传递给执行器:
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())
推荐阅读
- reactjs - 只返回一次但返回多次
- spacemacs - 将 gnuplot 包添加到 Spacemacs
- c++ - 使用 {fmt} 库打印
- c# - 如何正确实现图像数据迁移的 async/await?
- java - 带有 2 个数组列表的 Java toString
- robotframework - 机器人框架:两个循环迭代不同的值并根据这些值写入预期的 Excel 行
- amazon-web-services - ALB 抱怨 cognito
- r - 如何在 r 中从 USDA 获取数据
- javascript - 如何使用 Xcode/Instruments/Time Profiler 分析 React Native 源代码
- c - 如何将字符“G”转换为字符串“47”(十六进制)