![](/img/trans.png)
[英]sendMessage from outside in autobahn running in separate thread by using “asyncio”
[英]Python - Running Autobahn|Python asyncio websocket server in a separate subprocess or thread
我有一個基於tkinter的GUI程序在Python 3.4.1中運行。 我在程序中運行了幾個線程來從各個URL獲取JSON數據。 我想添加一些WebSocket功能,以便能夠允許程序充當服務器,並允許多個客戶端通過WebSocket連接到它並交換其他JSON數據。
我正在嘗試使用Autobahn | Python WebSocket服務器進行asyncio。
我首先嘗試在GUI程序下的單獨線程中運行asyncio事件循環。 但是,每次嘗試都會產生'AssertionError:線程'Thread-1'中沒有當前事件循環。
然后,我嘗試使用標准庫多處理程序包生成一個進程,該程序包在另一個進程中運行asyncio事件循環。 當我嘗試這個時,我沒有得到任何異常,但WebSocket服務器也沒有啟動。
甚至可以在另一個Python程序的子進程中運行asyncio事件循環嗎?
有沒有辦法將asyncio事件循環集成到當前多線程/ tkinter程序中?
更新下面是我嘗試運行初始測試的實際代碼。
from autobahn.asyncio.websocket import WebSocketServerProtocol
from autobahn.asyncio.websocket import WebSocketServerFactory
import asyncio
from multiprocessing import Process
class MyServerProtocol(WebSocketServerProtocol):
def onConnect(self, request):
print("Client connecting: {0}".format(request.peer))
def onOpen(self):
print("WebSocket connection open.")
def onMessage(self, payload, isBinary):
if isBinary:
print("Binary message received: {0} bytes".format(len(payload)))
else:
print("Text message received: {0}".format(payload.decode('utf8')))
## echo back message verbatim
self.sendMessage(payload, isBinary)
def onClose(self, wasClean, code, reason):
print("WebSocket connection closed: {0}".format(reason))
def start_server():
factory = WebSocketServerFactory("ws://10.241.142.27:6900", debug = False)
factory.protocol = MyServerProtocol
loop = asyncio.get_event_loop()
coro = loop.create_server(factory, '10.241.142.27', 6900)
server = loop.run_until_complete(coro)
loop.run_forever()
server.close()
loop.close()
websocket_server_process = Process(target = start_server)
websocket_server_process.start()
其中大部分直接來自asobio的Autobahn | Python示例代碼。 如果我嘗試將它作為進程運行它沒有做任何事情,沒有客戶端可以連接到它,如果我運行netstat -a沒有使用端口6900。 如果只在主程序中使用start_server(),則會創建WebSocket服務器。
首先,你得到的是AssertionError: There is no current event loop in thread 'Thread-1'.
因為asyncio
要求程序中的每個線程都有自己的事件循環,但它只會在主線程中自動為您創建一個事件循環。 因此,如果在主線程中調用asyncio.get_event_loop
一次,它將自動創建一個循環對象並將其設置為默認值,但如果在子線程中再次調用它,則會出現該錯誤。 相反,您需要在線程啟動時顯式創建/設置事件循環:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
完成后,您應該能夠在該特定線程中使用get_event_loop()
。
可以在通過multiprocessing
啟動的子asyncio
啟動asyncio
事件循環:
import asyncio
from multiprocessing import Process
@asyncio.coroutine
def coro():
print("hi")
def worker():
loop = asyncio.get_event_loop()
loop.run_until_complete(coro())
if __name__ == "__main__":
p = Process(target=worker)
p.start()
p.join()
輸出:
hi
唯一需要注意的是,如果您在父進程和子進程中啟動事件循環,則需要在Unix平台上顯式創建/設置新的事件循環(由於Python中的錯誤) )。 它應該在Windows上正常工作,或者如果你使用'spawn' multiprocessing
上下文。
我認為這應該有可能啟動asyncio
在你的Tkinter應用程序的后台線程(或程序)事件循環,並同時擁有tkinter
和asyncio
事件循環運行並排側。 如果您嘗試從后台線程/進程更新GUI,則只會遇到問題。
@dano的答案可能是正確的,但是創建了一個在大多數情況下都是無關緊要的新流程。
我在Google上發現了這個問題,因為我自己也有同樣的問題。 我寫了一個應用程序,我希望websocket api不在主線程上運行,這導致了你的問題。
我通過簡單地閱讀python文檔中的事件循環找到了我的替代sollution,並找到了解決此問題的asyncio.new_event_loop和asyncio.set_event_loop函數。
我沒有使用AutoBahn,而是使用pypi websockets庫,這是我的解決方案
import websockets
import asyncio
import threading
class WebSocket(threading.Thread):
@asyncio.coroutine
def handler(self, websocket, path):
name = yield from websocket.recv()
print("< {}".format(name))
greeting = "Hello {}!".format(name)
yield from websocket.send(greeting)
print("> {}".format(greeting))
def run(self):
start_server = websockets.serve(self.handler, '127.0.0.1', 9091)
eventloop = asyncio.new_event_loop()
asyncio.set_event_loop(eventloop)
eventloop.run_until_complete(start_server)
eventloop.run_forever()
if __name__ == "__main__":
ws = WebSocket()
ws.start()
“有沒有辦法將asyncio事件循環集成到當前的多線程/ tkinter程序中?”
是的,使用asyncio事件循環運行您的tkinter程序。 概念證明。
'''Proof of concept integrating asyncio and tk loops.
Terry Jan Reedy
Run with 'python -i' or from IDLE editor to keep tk window alive.
'''
import asyncio
import datetime as dt
import tkinter as tk
loop = asyncio.get_event_loop()
root = tk.Tk()
# Combine 2 event loop examples from BaseEventLoop doc.
# Add button to prove that gui remain responsive between time updates.
# Prints statements are only for testing.
def flipbg(widget, color):
bg = widget['bg']
print('click', bg, loop.time())
widget['bg'] = color if bg == 'white' else 'white'
hello = tk.Label(root)
flipper = tk.Button(root, text='Change hello background', bg='yellow',
command=lambda: flipbg(hello, 'red'))
time = tk.Label(root)
hello.pack()
flipper.pack()
time.pack()
def hello_world(loop):
hello['text'] = 'Hello World'
loop.call_soon(hello_world, loop)
def display_date(end_time, loop):
print(dt.datetime.now())
time['text'] = dt.datetime.now()
if (loop.time() + 1.0) < end_time:
loop.call_later(1, display_date, end_time, loop)
else:
loop.stop()
end_time = loop.time() + 10.1
loop.call_soon(display_date, end_time, loop)
# Replace root.mainloop with these 4 lines.
def tk_update():
root.update()
loop.call_soon(tk_update) # or loop.call_later(delay, tk_update)
# Initialize loop before each run_forever or run_until_complete call
tk_update()
loop.run_forever()
我已經通過實驗運行IDLE和那4個額外的行,當語法突出顯示1000行時,減速僅顯着。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.