Python – запуск Autobahn | сервер Python asyncio websocket в отдельном подпроцессе или потоке

У меня есть программа GUI на основе tkinter, работающая в Python 3.4.1. У меня есть несколько потоков, запущенных в программе, чтобы получать данные JSON из разных URL-адресов. Я хочу добавить некоторые функции WebSocket, чтобы позволить программе действовать как сервер и разрешать нескольким клиентам подключаться к ней через WebSocket и обмениваться другими данными JSON.

Я пытаюсь использовать сервер Autobahn | Python WebSocket для asyncio.

Сначала я попытался запустить цикл событий asyncio в отдельном потоке в программе GUI. Тем не менее, каждая попытка дает «AssertionError: в потоке« Thread-1 »отсутствует текущий цикл событий.

Затем я попытался создать процесс со стандартным пакетом многопроцессорной библиотеки, который запускал цикл событий asyncio в другом Процессе. Когда я пытаюсь это сделать, я не получаю никаких исключений, но сервер WebSocket также не запускается.

Возможно ли даже запустить цикл событий asyncio в подпроцессе из другой программы Python?

Есть ли способ интегрировать цикл событий asyncio в текущую многопоточную / tkinter-программу?

UPDATE Ниже приведен фактический код, который я пытаюсь запустить для первоначального теста.

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() 

Большинство из них прямо из примера кода Autobahn | Python для asyncio. Если я попытаюсь запустить его как процесс, он ничего не сделает, ни один клиент не сможет подключиться к нему, если я запустил netstat -a, не используется порт 6900. Если вы просто используете start_server () в основной программе, он создает сервер WebSocket.

3 Solutions collect form web for “Python – запуск Autobahn | сервер Python asyncio 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() в этом конкретном потоке.

Можно запустить asyncio событий asyncio в подпроцессе, запущенном с помощью multiprocessing :

 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 или использовать multiprocessing контекст «spawn».

Я думаю, что должно быть возможно запустить asyncio событий asyncio в фоновом потоке (или процессе) вашего приложения Tkinter и иметь asyncio событий tkinter и asyncio бок о бок. Вы столкнетесь с проблемами, если попытаетесь обновить графический интерфейс из фонового потока / процесса.

Ответ @dano может быть правильным, но создает новый процесс, который в большинстве случаев является неуместным.

Я нашел этот вопрос в Google, потому что у меня была такая же проблема. Я написал приложение, в котором я хотел, чтобы aps веб-приложения не запускались в основном потоке, и это вызвало вашу проблему.

Я нашел свое альтернативное решение, просто прочитав о циклах событий в документации на python и нашел функции 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-программу?»

Да, запустите программу tkinter с помощью цикла событий asyncio. Доказательство концепции.

 '''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 строк.

Python - лучший язык программирования в мире.