Why can asyncio event loop sometimes finish a task even when encountering `RuntimeError`?
up vote
1
down vote
favorite
I've been playing around with Python's asyncio. I think I have a reasonable understanding by now. But the following behavior puzzles me.
test.py:
from threading import Thread
import asyncio
async def wait(t):
await asyncio.sleep(t)
print(f'waited {t} sec')
def run(loop):
loop.run_until_complete(wait(2))
loop = asyncio.get_event_loop()
t = Thread(target=run, args=(loop,))
t.start()
loop.run_until_complete(wait(1))
t.join()
This code is wrong. I know that. The event loop can't be run while it's running, and it's generally not thread safe.
My question: why can wait(1) sometimes still finish its job?
Here's the output from two consecutive runs:
>>> py test.py
... Traceback (most recent call last):
... File "test.py", line 14, in <module>
... loop.run_until_complete(wait(1))
... File "C:PythonPython37libasynciobase_events.py", line 555, in run_until_complete
... self.run_forever()
... File "C:PythonPython37libasynciobase_events.py", line 510, in run_forever
...
... raise RuntimeError('This event loop is already running')
... RuntimeError: This event loop is already running
... waited 2 sec
>>> py test.py
... Traceback (most recent call last):
... File "test.py", line 14, in <module>
... loop.run_until_complete(wait(1))
... File "C:PythonPython37libasynciobase_events.py", line 555, in run_until_c
... omplete
... self.run_forever()
... File "C:PythonPython37libasynciobase_events.py", line 510, in run_forever
...
... raise RuntimeError('This event loop is already running')
... RuntimeError: This event loop is already running
... waited 1 sec
... waited 2 sec
The first run's behavior is what I expected - the main thread fails, but the event loop still runs wait(2) to finish in the thread t.
The second run is puzzling, how can wait(1) do its job when the RuntimeError is already thrown? I guess it has to do with thread synchronization and the non-thread-safe nature of the event loop. But I don't know exactly how this works.
python python-asyncio
add a comment |
up vote
1
down vote
favorite
I've been playing around with Python's asyncio. I think I have a reasonable understanding by now. But the following behavior puzzles me.
test.py:
from threading import Thread
import asyncio
async def wait(t):
await asyncio.sleep(t)
print(f'waited {t} sec')
def run(loop):
loop.run_until_complete(wait(2))
loop = asyncio.get_event_loop()
t = Thread(target=run, args=(loop,))
t.start()
loop.run_until_complete(wait(1))
t.join()
This code is wrong. I know that. The event loop can't be run while it's running, and it's generally not thread safe.
My question: why can wait(1) sometimes still finish its job?
Here's the output from two consecutive runs:
>>> py test.py
... Traceback (most recent call last):
... File "test.py", line 14, in <module>
... loop.run_until_complete(wait(1))
... File "C:PythonPython37libasynciobase_events.py", line 555, in run_until_complete
... self.run_forever()
... File "C:PythonPython37libasynciobase_events.py", line 510, in run_forever
...
... raise RuntimeError('This event loop is already running')
... RuntimeError: This event loop is already running
... waited 2 sec
>>> py test.py
... Traceback (most recent call last):
... File "test.py", line 14, in <module>
... loop.run_until_complete(wait(1))
... File "C:PythonPython37libasynciobase_events.py", line 555, in run_until_c
... omplete
... self.run_forever()
... File "C:PythonPython37libasynciobase_events.py", line 510, in run_forever
...
... raise RuntimeError('This event loop is already running')
... RuntimeError: This event loop is already running
... waited 1 sec
... waited 2 sec
The first run's behavior is what I expected - the main thread fails, but the event loop still runs wait(2) to finish in the thread t.
The second run is puzzling, how can wait(1) do its job when the RuntimeError is already thrown? I guess it has to do with thread synchronization and the non-thread-safe nature of the event loop. But I don't know exactly how this works.
python python-asyncio
1
Because threading is unsafe but sometimes you get lucky.
– Martijn Pieters♦
Nov 7 at 16:46
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I've been playing around with Python's asyncio. I think I have a reasonable understanding by now. But the following behavior puzzles me.
test.py:
from threading import Thread
import asyncio
async def wait(t):
await asyncio.sleep(t)
print(f'waited {t} sec')
def run(loop):
loop.run_until_complete(wait(2))
loop = asyncio.get_event_loop()
t = Thread(target=run, args=(loop,))
t.start()
loop.run_until_complete(wait(1))
t.join()
This code is wrong. I know that. The event loop can't be run while it's running, and it's generally not thread safe.
My question: why can wait(1) sometimes still finish its job?
Here's the output from two consecutive runs:
>>> py test.py
... Traceback (most recent call last):
... File "test.py", line 14, in <module>
... loop.run_until_complete(wait(1))
... File "C:PythonPython37libasynciobase_events.py", line 555, in run_until_complete
... self.run_forever()
... File "C:PythonPython37libasynciobase_events.py", line 510, in run_forever
...
... raise RuntimeError('This event loop is already running')
... RuntimeError: This event loop is already running
... waited 2 sec
>>> py test.py
... Traceback (most recent call last):
... File "test.py", line 14, in <module>
... loop.run_until_complete(wait(1))
... File "C:PythonPython37libasynciobase_events.py", line 555, in run_until_c
... omplete
... self.run_forever()
... File "C:PythonPython37libasynciobase_events.py", line 510, in run_forever
...
... raise RuntimeError('This event loop is already running')
... RuntimeError: This event loop is already running
... waited 1 sec
... waited 2 sec
The first run's behavior is what I expected - the main thread fails, but the event loop still runs wait(2) to finish in the thread t.
The second run is puzzling, how can wait(1) do its job when the RuntimeError is already thrown? I guess it has to do with thread synchronization and the non-thread-safe nature of the event loop. But I don't know exactly how this works.
python python-asyncio
I've been playing around with Python's asyncio. I think I have a reasonable understanding by now. But the following behavior puzzles me.
test.py:
from threading import Thread
import asyncio
async def wait(t):
await asyncio.sleep(t)
print(f'waited {t} sec')
def run(loop):
loop.run_until_complete(wait(2))
loop = asyncio.get_event_loop()
t = Thread(target=run, args=(loop,))
t.start()
loop.run_until_complete(wait(1))
t.join()
This code is wrong. I know that. The event loop can't be run while it's running, and it's generally not thread safe.
My question: why can wait(1) sometimes still finish its job?
Here's the output from two consecutive runs:
>>> py test.py
... Traceback (most recent call last):
... File "test.py", line 14, in <module>
... loop.run_until_complete(wait(1))
... File "C:PythonPython37libasynciobase_events.py", line 555, in run_until_complete
... self.run_forever()
... File "C:PythonPython37libasynciobase_events.py", line 510, in run_forever
...
... raise RuntimeError('This event loop is already running')
... RuntimeError: This event loop is already running
... waited 2 sec
>>> py test.py
... Traceback (most recent call last):
... File "test.py", line 14, in <module>
... loop.run_until_complete(wait(1))
... File "C:PythonPython37libasynciobase_events.py", line 555, in run_until_c
... omplete
... self.run_forever()
... File "C:PythonPython37libasynciobase_events.py", line 510, in run_forever
...
... raise RuntimeError('This event loop is already running')
... RuntimeError: This event loop is already running
... waited 1 sec
... waited 2 sec
The first run's behavior is what I expected - the main thread fails, but the event loop still runs wait(2) to finish in the thread t.
The second run is puzzling, how can wait(1) do its job when the RuntimeError is already thrown? I guess it has to do with thread synchronization and the non-thread-safe nature of the event loop. But I don't know exactly how this works.
python python-asyncio
python python-asyncio
asked Nov 7 at 16:45
Chenfeng
17118
17118
1
Because threading is unsafe but sometimes you get lucky.
– Martijn Pieters♦
Nov 7 at 16:46
add a comment |
1
Because threading is unsafe but sometimes you get lucky.
– Martijn Pieters♦
Nov 7 at 16:46
1
1
Because threading is unsafe but sometimes you get lucky.
– Martijn Pieters♦
Nov 7 at 16:46
Because threading is unsafe but sometimes you get lucky.
– Martijn Pieters♦
Nov 7 at 16:46
add a comment |
2 Answers
2
active
oldest
votes
up vote
1
down vote
Ohhh... never mind. I read the code of asyncio and figured it out. It's actually quite simple.
run_until_complete calls ensure_future(future, loop=self) before it checks self.is_running() (which is done in run_forever). Since the loop is already running, it can pick up the task before the RuntimeError is thrown. Of course it doesn't always happen because of the race condition.
add a comment |
up vote
1
down vote
Exceptions are thrown per thread. The runtime error is raised in a different thread from the event loop. The event loop continues to execute, regardless.
And wait(1) can sometimes finish it's job because you can get lucky. The asyncio loop internal data structures are not guarded against race conditions caused by using threads (which is why there are specific thread-support methods you should use instead). But the nature of race conditions is such that it depends on the exact order of events and that order can change each time you run your program, depending on what else your OS is doing at the time.
The run_until_complete() method first calls asyncio.ensure_task() to add the coroutine to the task queue with a 'done' callback attached that will stop the event loop again, then calls loop.run_forever(). When the coroutine returns, the callback stops the loop. The loop.run_forever() call throws the RuntimeError here.
When you do this from a thread, the task gets added to a deque object attached to the loop, and if that happens at the right moment (e.g. when the running loop is not busy emptying the queue), the running loop in the main thread will find it, and execute it, even if the loop.run_forever() call raised an exception.
All this relies on implementation details. Different versions of Python will probably exhibit different behaviour here, and if you install an alternative loop (e.g. uvloop), there will almost certainly be different behaviour again.
If you want to schedule coroutines from a different thread, use asyncio.run_coroutine_threadsafe(); it would :
from threading import Thread
import asyncio
async def wait(t):
print(f'going to wait {t} seconds')
await asyncio.sleep(t)
print(f'waited {t} sec')
def run(loop):
asyncio.run_coroutine_threadsafe(wait(2), loop)
loop = asyncio.get_event_loop()
t = Thread(target=run, args=(loop,))
t.start()
loop.run_until_complete(wait(1))
t.join()
The above doesn't actually complete the wait(2) coroutine because the wait(1) coroutine is being run with loop.run_until_complete() so its callback stops the loop again before the 2 second wait is over. But the coroutine is actually started:
going to wait 1 seconds
going to wait 2 seconds
waited 1 sec
but if you made the main-thread coroutine take longer (with, say, wait(3)) then the one scheduled from the thread would also complete. You'd have to do additional work to ensure that there are no more pending tasks scheduled to run with the loop before you shut it down.
That was my vague guess as well, but I'm asking for a more specific answer. I've actually figured it out. Please see my answer.
– Chenfeng
Nov 7 at 16:55
@Chenfeng: that's exactly what my answer is saying :-)
– Martijn Pieters♦
Nov 7 at 16:59
Oh well... I didn't see your edits before I posted mine. Thanks anyway.
– Chenfeng
Nov 7 at 17:00
1
On your additional info: I find it better to callrun_foreverin a separate thread, and schedule tasks onto the loop from other threads. Once all tasks are completed as indicated by theconcurrent.futures.Futurereturned byrun_coroutine_threadsafe, useloop.call_soon_threadsafe(loop.stop)to stop the loop, and usethread.join()to make sure the loop has actually stopped. This ensures that all tasks are done, and all events happen in proper order. No guessing game.
– Chenfeng
Nov 7 at 18:15
@Chenfeng: closely related: How can you wait for completion of a callback submitted from another thread?.thread.join()is probably fine if all the thread does is run the loop.
– Martijn Pieters♦
Nov 7 at 18:17
|
show 2 more comments
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
Ohhh... never mind. I read the code of asyncio and figured it out. It's actually quite simple.
run_until_complete calls ensure_future(future, loop=self) before it checks self.is_running() (which is done in run_forever). Since the loop is already running, it can pick up the task before the RuntimeError is thrown. Of course it doesn't always happen because of the race condition.
add a comment |
up vote
1
down vote
Ohhh... never mind. I read the code of asyncio and figured it out. It's actually quite simple.
run_until_complete calls ensure_future(future, loop=self) before it checks self.is_running() (which is done in run_forever). Since the loop is already running, it can pick up the task before the RuntimeError is thrown. Of course it doesn't always happen because of the race condition.
add a comment |
up vote
1
down vote
up vote
1
down vote
Ohhh... never mind. I read the code of asyncio and figured it out. It's actually quite simple.
run_until_complete calls ensure_future(future, loop=self) before it checks self.is_running() (which is done in run_forever). Since the loop is already running, it can pick up the task before the RuntimeError is thrown. Of course it doesn't always happen because of the race condition.
Ohhh... never mind. I read the code of asyncio and figured it out. It's actually quite simple.
run_until_complete calls ensure_future(future, loop=self) before it checks self.is_running() (which is done in run_forever). Since the loop is already running, it can pick up the task before the RuntimeError is thrown. Of course it doesn't always happen because of the race condition.
edited Nov 7 at 16:59
answered Nov 7 at 16:54
Chenfeng
17118
17118
add a comment |
add a comment |
up vote
1
down vote
Exceptions are thrown per thread. The runtime error is raised in a different thread from the event loop. The event loop continues to execute, regardless.
And wait(1) can sometimes finish it's job because you can get lucky. The asyncio loop internal data structures are not guarded against race conditions caused by using threads (which is why there are specific thread-support methods you should use instead). But the nature of race conditions is such that it depends on the exact order of events and that order can change each time you run your program, depending on what else your OS is doing at the time.
The run_until_complete() method first calls asyncio.ensure_task() to add the coroutine to the task queue with a 'done' callback attached that will stop the event loop again, then calls loop.run_forever(). When the coroutine returns, the callback stops the loop. The loop.run_forever() call throws the RuntimeError here.
When you do this from a thread, the task gets added to a deque object attached to the loop, and if that happens at the right moment (e.g. when the running loop is not busy emptying the queue), the running loop in the main thread will find it, and execute it, even if the loop.run_forever() call raised an exception.
All this relies on implementation details. Different versions of Python will probably exhibit different behaviour here, and if you install an alternative loop (e.g. uvloop), there will almost certainly be different behaviour again.
If you want to schedule coroutines from a different thread, use asyncio.run_coroutine_threadsafe(); it would :
from threading import Thread
import asyncio
async def wait(t):
print(f'going to wait {t} seconds')
await asyncio.sleep(t)
print(f'waited {t} sec')
def run(loop):
asyncio.run_coroutine_threadsafe(wait(2), loop)
loop = asyncio.get_event_loop()
t = Thread(target=run, args=(loop,))
t.start()
loop.run_until_complete(wait(1))
t.join()
The above doesn't actually complete the wait(2) coroutine because the wait(1) coroutine is being run with loop.run_until_complete() so its callback stops the loop again before the 2 second wait is over. But the coroutine is actually started:
going to wait 1 seconds
going to wait 2 seconds
waited 1 sec
but if you made the main-thread coroutine take longer (with, say, wait(3)) then the one scheduled from the thread would also complete. You'd have to do additional work to ensure that there are no more pending tasks scheduled to run with the loop before you shut it down.
That was my vague guess as well, but I'm asking for a more specific answer. I've actually figured it out. Please see my answer.
– Chenfeng
Nov 7 at 16:55
@Chenfeng: that's exactly what my answer is saying :-)
– Martijn Pieters♦
Nov 7 at 16:59
Oh well... I didn't see your edits before I posted mine. Thanks anyway.
– Chenfeng
Nov 7 at 17:00
1
On your additional info: I find it better to callrun_foreverin a separate thread, and schedule tasks onto the loop from other threads. Once all tasks are completed as indicated by theconcurrent.futures.Futurereturned byrun_coroutine_threadsafe, useloop.call_soon_threadsafe(loop.stop)to stop the loop, and usethread.join()to make sure the loop has actually stopped. This ensures that all tasks are done, and all events happen in proper order. No guessing game.
– Chenfeng
Nov 7 at 18:15
@Chenfeng: closely related: How can you wait for completion of a callback submitted from another thread?.thread.join()is probably fine if all the thread does is run the loop.
– Martijn Pieters♦
Nov 7 at 18:17
|
show 2 more comments
up vote
1
down vote
Exceptions are thrown per thread. The runtime error is raised in a different thread from the event loop. The event loop continues to execute, regardless.
And wait(1) can sometimes finish it's job because you can get lucky. The asyncio loop internal data structures are not guarded against race conditions caused by using threads (which is why there are specific thread-support methods you should use instead). But the nature of race conditions is such that it depends on the exact order of events and that order can change each time you run your program, depending on what else your OS is doing at the time.
The run_until_complete() method first calls asyncio.ensure_task() to add the coroutine to the task queue with a 'done' callback attached that will stop the event loop again, then calls loop.run_forever(). When the coroutine returns, the callback stops the loop. The loop.run_forever() call throws the RuntimeError here.
When you do this from a thread, the task gets added to a deque object attached to the loop, and if that happens at the right moment (e.g. when the running loop is not busy emptying the queue), the running loop in the main thread will find it, and execute it, even if the loop.run_forever() call raised an exception.
All this relies on implementation details. Different versions of Python will probably exhibit different behaviour here, and if you install an alternative loop (e.g. uvloop), there will almost certainly be different behaviour again.
If you want to schedule coroutines from a different thread, use asyncio.run_coroutine_threadsafe(); it would :
from threading import Thread
import asyncio
async def wait(t):
print(f'going to wait {t} seconds')
await asyncio.sleep(t)
print(f'waited {t} sec')
def run(loop):
asyncio.run_coroutine_threadsafe(wait(2), loop)
loop = asyncio.get_event_loop()
t = Thread(target=run, args=(loop,))
t.start()
loop.run_until_complete(wait(1))
t.join()
The above doesn't actually complete the wait(2) coroutine because the wait(1) coroutine is being run with loop.run_until_complete() so its callback stops the loop again before the 2 second wait is over. But the coroutine is actually started:
going to wait 1 seconds
going to wait 2 seconds
waited 1 sec
but if you made the main-thread coroutine take longer (with, say, wait(3)) then the one scheduled from the thread would also complete. You'd have to do additional work to ensure that there are no more pending tasks scheduled to run with the loop before you shut it down.
That was my vague guess as well, but I'm asking for a more specific answer. I've actually figured it out. Please see my answer.
– Chenfeng
Nov 7 at 16:55
@Chenfeng: that's exactly what my answer is saying :-)
– Martijn Pieters♦
Nov 7 at 16:59
Oh well... I didn't see your edits before I posted mine. Thanks anyway.
– Chenfeng
Nov 7 at 17:00
1
On your additional info: I find it better to callrun_foreverin a separate thread, and schedule tasks onto the loop from other threads. Once all tasks are completed as indicated by theconcurrent.futures.Futurereturned byrun_coroutine_threadsafe, useloop.call_soon_threadsafe(loop.stop)to stop the loop, and usethread.join()to make sure the loop has actually stopped. This ensures that all tasks are done, and all events happen in proper order. No guessing game.
– Chenfeng
Nov 7 at 18:15
@Chenfeng: closely related: How can you wait for completion of a callback submitted from another thread?.thread.join()is probably fine if all the thread does is run the loop.
– Martijn Pieters♦
Nov 7 at 18:17
|
show 2 more comments
up vote
1
down vote
up vote
1
down vote
Exceptions are thrown per thread. The runtime error is raised in a different thread from the event loop. The event loop continues to execute, regardless.
And wait(1) can sometimes finish it's job because you can get lucky. The asyncio loop internal data structures are not guarded against race conditions caused by using threads (which is why there are specific thread-support methods you should use instead). But the nature of race conditions is such that it depends on the exact order of events and that order can change each time you run your program, depending on what else your OS is doing at the time.
The run_until_complete() method first calls asyncio.ensure_task() to add the coroutine to the task queue with a 'done' callback attached that will stop the event loop again, then calls loop.run_forever(). When the coroutine returns, the callback stops the loop. The loop.run_forever() call throws the RuntimeError here.
When you do this from a thread, the task gets added to a deque object attached to the loop, and if that happens at the right moment (e.g. when the running loop is not busy emptying the queue), the running loop in the main thread will find it, and execute it, even if the loop.run_forever() call raised an exception.
All this relies on implementation details. Different versions of Python will probably exhibit different behaviour here, and if you install an alternative loop (e.g. uvloop), there will almost certainly be different behaviour again.
If you want to schedule coroutines from a different thread, use asyncio.run_coroutine_threadsafe(); it would :
from threading import Thread
import asyncio
async def wait(t):
print(f'going to wait {t} seconds')
await asyncio.sleep(t)
print(f'waited {t} sec')
def run(loop):
asyncio.run_coroutine_threadsafe(wait(2), loop)
loop = asyncio.get_event_loop()
t = Thread(target=run, args=(loop,))
t.start()
loop.run_until_complete(wait(1))
t.join()
The above doesn't actually complete the wait(2) coroutine because the wait(1) coroutine is being run with loop.run_until_complete() so its callback stops the loop again before the 2 second wait is over. But the coroutine is actually started:
going to wait 1 seconds
going to wait 2 seconds
waited 1 sec
but if you made the main-thread coroutine take longer (with, say, wait(3)) then the one scheduled from the thread would also complete. You'd have to do additional work to ensure that there are no more pending tasks scheduled to run with the loop before you shut it down.
Exceptions are thrown per thread. The runtime error is raised in a different thread from the event loop. The event loop continues to execute, regardless.
And wait(1) can sometimes finish it's job because you can get lucky. The asyncio loop internal data structures are not guarded against race conditions caused by using threads (which is why there are specific thread-support methods you should use instead). But the nature of race conditions is such that it depends on the exact order of events and that order can change each time you run your program, depending on what else your OS is doing at the time.
The run_until_complete() method first calls asyncio.ensure_task() to add the coroutine to the task queue with a 'done' callback attached that will stop the event loop again, then calls loop.run_forever(). When the coroutine returns, the callback stops the loop. The loop.run_forever() call throws the RuntimeError here.
When you do this from a thread, the task gets added to a deque object attached to the loop, and if that happens at the right moment (e.g. when the running loop is not busy emptying the queue), the running loop in the main thread will find it, and execute it, even if the loop.run_forever() call raised an exception.
All this relies on implementation details. Different versions of Python will probably exhibit different behaviour here, and if you install an alternative loop (e.g. uvloop), there will almost certainly be different behaviour again.
If you want to schedule coroutines from a different thread, use asyncio.run_coroutine_threadsafe(); it would :
from threading import Thread
import asyncio
async def wait(t):
print(f'going to wait {t} seconds')
await asyncio.sleep(t)
print(f'waited {t} sec')
def run(loop):
asyncio.run_coroutine_threadsafe(wait(2), loop)
loop = asyncio.get_event_loop()
t = Thread(target=run, args=(loop,))
t.start()
loop.run_until_complete(wait(1))
t.join()
The above doesn't actually complete the wait(2) coroutine because the wait(1) coroutine is being run with loop.run_until_complete() so its callback stops the loop again before the 2 second wait is over. But the coroutine is actually started:
going to wait 1 seconds
going to wait 2 seconds
waited 1 sec
but if you made the main-thread coroutine take longer (with, say, wait(3)) then the one scheduled from the thread would also complete. You'd have to do additional work to ensure that there are no more pending tasks scheduled to run with the loop before you shut it down.
edited Nov 7 at 17:11
answered Nov 7 at 16:50
Martijn Pieters♦
691k12923892232
691k12923892232
That was my vague guess as well, but I'm asking for a more specific answer. I've actually figured it out. Please see my answer.
– Chenfeng
Nov 7 at 16:55
@Chenfeng: that's exactly what my answer is saying :-)
– Martijn Pieters♦
Nov 7 at 16:59
Oh well... I didn't see your edits before I posted mine. Thanks anyway.
– Chenfeng
Nov 7 at 17:00
1
On your additional info: I find it better to callrun_foreverin a separate thread, and schedule tasks onto the loop from other threads. Once all tasks are completed as indicated by theconcurrent.futures.Futurereturned byrun_coroutine_threadsafe, useloop.call_soon_threadsafe(loop.stop)to stop the loop, and usethread.join()to make sure the loop has actually stopped. This ensures that all tasks are done, and all events happen in proper order. No guessing game.
– Chenfeng
Nov 7 at 18:15
@Chenfeng: closely related: How can you wait for completion of a callback submitted from another thread?.thread.join()is probably fine if all the thread does is run the loop.
– Martijn Pieters♦
Nov 7 at 18:17
|
show 2 more comments
That was my vague guess as well, but I'm asking for a more specific answer. I've actually figured it out. Please see my answer.
– Chenfeng
Nov 7 at 16:55
@Chenfeng: that's exactly what my answer is saying :-)
– Martijn Pieters♦
Nov 7 at 16:59
Oh well... I didn't see your edits before I posted mine. Thanks anyway.
– Chenfeng
Nov 7 at 17:00
1
On your additional info: I find it better to callrun_foreverin a separate thread, and schedule tasks onto the loop from other threads. Once all tasks are completed as indicated by theconcurrent.futures.Futurereturned byrun_coroutine_threadsafe, useloop.call_soon_threadsafe(loop.stop)to stop the loop, and usethread.join()to make sure the loop has actually stopped. This ensures that all tasks are done, and all events happen in proper order. No guessing game.
– Chenfeng
Nov 7 at 18:15
@Chenfeng: closely related: How can you wait for completion of a callback submitted from another thread?.thread.join()is probably fine if all the thread does is run the loop.
– Martijn Pieters♦
Nov 7 at 18:17
That was my vague guess as well, but I'm asking for a more specific answer. I've actually figured it out. Please see my answer.
– Chenfeng
Nov 7 at 16:55
That was my vague guess as well, but I'm asking for a more specific answer. I've actually figured it out. Please see my answer.
– Chenfeng
Nov 7 at 16:55
@Chenfeng: that's exactly what my answer is saying :-)
– Martijn Pieters♦
Nov 7 at 16:59
@Chenfeng: that's exactly what my answer is saying :-)
– Martijn Pieters♦
Nov 7 at 16:59
Oh well... I didn't see your edits before I posted mine. Thanks anyway.
– Chenfeng
Nov 7 at 17:00
Oh well... I didn't see your edits before I posted mine. Thanks anyway.
– Chenfeng
Nov 7 at 17:00
1
1
On your additional info: I find it better to call
run_forever in a separate thread, and schedule tasks onto the loop from other threads. Once all tasks are completed as indicated by the concurrent.futures.Future returned by run_coroutine_threadsafe, use loop.call_soon_threadsafe(loop.stop) to stop the loop, and use thread.join() to make sure the loop has actually stopped. This ensures that all tasks are done, and all events happen in proper order. No guessing game.– Chenfeng
Nov 7 at 18:15
On your additional info: I find it better to call
run_forever in a separate thread, and schedule tasks onto the loop from other threads. Once all tasks are completed as indicated by the concurrent.futures.Future returned by run_coroutine_threadsafe, use loop.call_soon_threadsafe(loop.stop) to stop the loop, and use thread.join() to make sure the loop has actually stopped. This ensures that all tasks are done, and all events happen in proper order. No guessing game.– Chenfeng
Nov 7 at 18:15
@Chenfeng: closely related: How can you wait for completion of a callback submitted from another thread?.
thread.join() is probably fine if all the thread does is run the loop.– Martijn Pieters♦
Nov 7 at 18:17
@Chenfeng: closely related: How can you wait for completion of a callback submitted from another thread?.
thread.join() is probably fine if all the thread does is run the loop.– Martijn Pieters♦
Nov 7 at 18:17
|
show 2 more comments
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53194040%2fwhy-can-asyncio-event-loop-sometimes-finish-a-task-even-when-encountering-runti%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Because threading is unsafe but sometimes you get lucky.
– Martijn Pieters♦
Nov 7 at 16:46