Python 3.x Python websockets和gtk-对异步IO队列感到困惑

Python 3.x Python websockets和gtk-对异步IO队列感到困惑,python-3.x,websocket,gtk3,python-asyncio,Python 3.x,Websocket,Gtk3,Python Asyncio,我是python异步编程新手,我正在尝试编写一个脚本,该脚本启动websocket服务器,侦听消息,并在gtk窗口中触发某些事件(例如按下“s”键)时发送消息。以下是我目前掌握的情况: import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk import asyncio import websockets import threading ws = None async def consumer_han

我是python异步编程新手,我正在尝试编写一个脚本,该脚本启动websocket服务器,侦听消息,并在gtk窗口中触发某些事件(例如按下“s”键)时发送消息。以下是我目前掌握的情况:

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import asyncio
import websockets
import threading

ws = None

async def consumer_handler(websocket, path):
    global ws
    ws = websocket
    await websocket.send("Hello client")
    while True:
        message = await websocket.recv()
        print("Message from client: " + message)

def keypress(widget,event):
    global ws
    if event.keyval == 115 and ws: #s key pressed, connection open
        asyncio.get_event_loop().create_task(ws.send("key press"))
        print("Message to client: key press")

def quit(widget):
    Gtk.main_quit()

window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
window.connect("destroy", quit)
window.connect("key_press_event", keypress)
window.show()

start_server = websockets.serve(consumer_handler, 'localhost', 8765)
asyncio.get_event_loop().run_until_complete(start_server)
wst = threading.Thread(target=asyncio.get_event_loop().run_forever)
wst.daemon = True
wst.start()

Gtk.main()
以下是客户端网页:

<!DOCTYPE html>
<html>
    <head>
        <title>Websockets test page</title>
        <meta charset="UTF-8" />
        <script>
var exampleSocket = new WebSocket("ws://localhost:8765");

function mylog(msg) {
    document.getElementById("log").innerHTML += msg + "<br/>";
}

function send() {
    mylog("Message to server: Hello server");
    exampleSocket.send("Hello server"); 
}

exampleSocket.onopen = function (event) {
    mylog("Connection opened");
};

exampleSocket.onmessage = function (event) {
    mylog("Message from server: " + event.data);
}
        </script>
    </head>
    <body>
        <p id="log"></p>
        <input type="button" value="Send message" onclick="send()"/>
    </body>
</html>

WebSocket测试页
var exampleSocket=newWebSocket(“ws://localhost:8765”);
函数mylog(msg){
document.getElementById(“log”).innerHTML+=msg+“
”; } 函数send(){ mylog(“发送给服务器的消息:Hello server”); 示例socket.send(“Hello服务器”); } exampleSocket.onopen=函数(事件){ mylog(“连接已打开”); }; exampleSocket.onmessage=函数(事件){ mylog(“来自服务器的消息:+event.data”); }

运行python代码,然后在浏览器中加载网页,现在浏览器发送的任何消息都会显示在python标准输出中,到目前为止效果良好。但是,如果您在gtk窗口中点击“s”键,python将不会发送消息,直到从浏览器接收到另一条消息(通过按下“发送消息”按钮)。我以为
等待websocket.recv()
是为了在收到消息之前将控制权返回到事件循环?如何让它在等待接收时发送消息

但是,如果在gtk窗口中点击“s”键,python将在收到来自浏览器的另一条消息之前不发送该消息

问题出在这一行:

asyncio.get_event_loop().create_task(ws.send("key press"))
由于asyncio事件循环和GTK主循环在不同的线程中运行,因此需要使用将协程提交给asyncio。比如:

asyncio.run_coroutine_threadsafe(ws.send("key press"), loop)
create\u task
将协同路由添加到可运行的协同路由队列中,但无法唤醒事件循环,这就是为什么只有在异步IO中发生其他事件时才运行协同路由。另外,
create_task
不是线程安全的,因此在事件循环本身修改运行队列时调用它可能会损坏其数据结构
run\u coroutine\u threadsafe
没有这两个问题,它安排事件循环尽快唤醒,并使用互斥来保护事件循环的数据结构