Re: asyncio - how to stop loop?
Ian Kelly ian.g.ke...@gmail.com wrote in message news:CALwzidnv07Wba9WJ=nuc0_v4mvudyaxwh6bgjvw0o1hf3oo...@mail.gmail.com... On Wed, Jun 11, 2014 at 1:19 AM, Frank Millman fr...@chagford.com wrote: First attempt - same as before loop = asyncio.get_event_loop() threading.Thread(target=loop.run_forever).start() input('Press enter to stop') loop.stop() loop.close() Each event loop is hosted by a specific thread. In this case you're getting the event loop of the main thread and then trying to run it in a separate thread, which is not a good idea. You can run an event loop in a separate thread, but you should install a separate event loop for that thread if you do (and then when you interact with the loop, do so in a thread-safe manner -- see below). Second attempt - move the keyboard input to a separate thread def stop_loop(): input('Press enter to stop') loop.stop() loop.close() loop = asyncio.get_event_loop() threading.Thread(target=stop_loop).start() loop.run_forever() One issue here is that (like most event loop implementations) event loops are not thread-safe. To make a call to the event loop across threads, you should be using the call_soon_threadsafe method, e.g. loop.call_soon_threadsafe(loop.stop). You'll also want to make sure that the event loop has actually stopped before you call loop.close -- see below. Third attempt - get the loop to close itself (cannot use in practice, but see what happens) def stop_loop(): loop.stop() loop.close() loop = asyncio.get_event_loop() loop.call_later(2, stop_loop) loop.run_forever() I think what's happening here is that per the docs loop.close should not be called while the loop is running. You've called loop.stop but you're still inside a callback, which is a bit of a gray area. You probably don't need to call loop.close at all, but if you want to do so I suggest putting it after the run_forever call, so you can be certain the loop has stopped when it's called. Putting all that together, you should have something like this: def stop_loop(): input('Press enter to stop') loop.call_soon_threadsafe(loop.stop) loop = asyncio.get_event_loop() threading.Thread(target=stop_loop).start() loop.run_forever() loop.close() # optional Thanks very much for the very clear explanation. Your solution works perfectly. Much appreciated. Frank -- https://mail.python.org/mailman/listinfo/python-list
asyncio - how to stop loop?
Hi all I have a 'start.py' script that kicks off my program by starting a number of services. For now, I stop it by allowing the user to press enter, after which I close down the services and stop the program. (For production I imagine it would be better to run it in the background and send it some kind of signal to terminate, but for testing this suffices.) As one of the services is a 'listening' loop, which would normally prevent access to the keyboard, I start it in a separate thread. For example, when I was using cherrypy as the server, I had - server = cherrypy.wsgiserver.CherryPyWSGIServer((host, port), dispatcher) threading.Thread(target=server.start).start() input('Press enter to stop') server.stop() Now I am playing with asyncio, but I cannot get it to behave the same. Here are my attempts. I use python 3.4.1, and I tested on Windows and Linux - the results are different on each platform. First attempt - same as before loop = asyncio.get_event_loop() threading.Thread(target=loop.run_forever).start() input('Press enter to stop') loop.stop() loop.close() Windows traceback - Exception in thread Thread-1: Traceback (most recent call last): File C:\Python34\lib\threading.py, line 920, in _bootstrap_inner self.run() File C:\Python34\lib\threading.py, line 868, in run self._target(*self._args, **self._kwargs) File C:\Python34\lib\asyncio\base_events.py, line 184, in run_forever self._run_once() File C:\Python34\lib\asyncio\base_events.py, line 795, in _run_once event_list = self._selector.select(timeout) AttributeError: 'NoneType' object has no attribute 'select' With Linux, it does not terminate. If I press Ctrl_C, I get this traceback (hope there are no typos) - Traceback (most recent call last): File /usr/local/lib/python3.4/threading.py, line 1294, in _shutdown t.join() File /usr/local/lib/python3.4/threading.py, line 1060, in join self._wait_for_tstate_lock() File /usr/local/lib/python3.4/threading.py, line 1076, in _wait_for_tstate_lock elif lock.acquire(block, timeout): KeyboardInterrupt Second attempt - move the keyboard input to a separate thread def stop_loop(): input('Press enter to stop') loop.stop() loop.close() loop = asyncio.get_event_loop() threading.Thread(target=stop_loop).start() loop.run_forever() With Windows, it works - the program closes cleanly. With Linux, it does not terminate. If I press Ctrl_C, I get this traceback (hope there are no typos) - Traceback (most recent call last): File test_async.py, line 20, in module loop.run_forever() File /usr/local/lib/python3.4/asyncio.base_events.py, line 184, in run_forever self._run_once() File /usr/local/lib/python3.4/asyncio.base_events.py, line 795, in _run_once event_list = self._selector.select(timeout) File /usr/local/lib/python3.4/asyncio.selectors.py, line 424, in select fd_event_list = self._epoll.poll(timeout, max_cv) KeyboardInterrupt Third attempt - get the loop to close itself (cannot use in practice, but see what happens) def stop_loop(): loop.stop() loop.close() loop = asyncio.get_event_loop() loop.call_later(2, stop_loop) loop.run_forever() Both platforms show the same traceback, after two seconds - Traceback (most recent call last): File C:\Documents and Settings\frank\aib\aib_async\test_async.py, line 29, in module loop.run_forever() File C:\Python34\lib\asyncio\base_events.py, line 184, in run_forever self._run_once() File C:\Python34\lib\asyncio\base_events.py, line 795, in _run_once event_list = self._selector.select(timeout) AttributeError: 'NoneType' object has no attribute 'select' Can anyone suggest a way to get the loop to stop cleanly? Thanks Frank Millman -- https://mail.python.org/mailman/listinfo/python-list
Re: asyncio - how to stop loop?
On Wed, Jun 11, 2014 at 1:19 AM, Frank Millman fr...@chagford.com wrote: First attempt - same as before loop = asyncio.get_event_loop() threading.Thread(target=loop.run_forever).start() input('Press enter to stop') loop.stop() loop.close() Each event loop is hosted by a specific thread. In this case you're getting the event loop of the main thread and then trying to run it in a separate thread, which is not a good idea. You can run an event loop in a separate thread, but you should install a separate event loop for that thread if you do (and then when you interact with the loop, do so in a thread-safe manner -- see below). Second attempt - move the keyboard input to a separate thread def stop_loop(): input('Press enter to stop') loop.stop() loop.close() loop = asyncio.get_event_loop() threading.Thread(target=stop_loop).start() loop.run_forever() One issue here is that (like most event loop implementations) event loops are not thread-safe. To make a call to the event loop across threads, you should be using the call_soon_threadsafe method, e.g. loop.call_soon_threadsafe(loop.stop). You'll also want to make sure that the event loop has actually stopped before you call loop.close -- see below. Third attempt - get the loop to close itself (cannot use in practice, but see what happens) def stop_loop(): loop.stop() loop.close() loop = asyncio.get_event_loop() loop.call_later(2, stop_loop) loop.run_forever() I think what's happening here is that per the docs loop.close should not be called while the loop is running. You've called loop.stop but you're still inside a callback, which is a bit of a gray area. You probably don't need to call loop.close at all, but if you want to do so I suggest putting it after the run_forever call, so you can be certain the loop has stopped when it's called. Putting all that together, you should have something like this: def stop_loop(): input('Press enter to stop') loop.call_soon_threadsafe(loop.stop) loop = asyncio.get_event_loop() threading.Thread(target=stop_loop).start() loop.run_forever() loop.close() # optional -- https://mail.python.org/mailman/listinfo/python-list