Reputation:
So i have a script for a countdown which looks something like this:
import time, threading, asyncio
def countdown(n, m):
print("timer start")
time.sleep(n)
print("timer stop")
yield coro1
async def coro1():
print("coroutine called")
async def coromain():
print("first")
t1 = threading.Thread(target=countdown, args=(5, 0))
t1.start()
print("second")
loop = asyncio.get_event_loop()
loop.run_until_complete(coromain())
loop.stop()
What i want it to do is simple:
Run coromain
Print "first"
Start thread t1, print "timer start" and have it wait for 5 seconds
In the mean time, print "second"
after 5 seconds, print "timer stop"
exit
However, when i run this code it outputs:
Run coromain
Print "first"
Print "second"
exit
I'm so confused as to why it does this. Can anyone explain what I'm doing wrong here?
Upvotes: 5
Views: 8094
Reputation: 492
I'm so confused as to why it does this. Can anyone explain what I'm doing wrong here?
There is already an accepted answer showing how to achieve what you want, but just to explain why your code produced the output it did:
The coroutine coromain
starts the countdown as a thread, but does not wait for it to finish. Actually your coroutine, and therefore your program, exits before the thread could be exectued.
Upvotes: 0
Reputation: 7920
This depends on whether your question is a part of a bigger problem imposing additional constraints or not, but I do not see a reason to use threading
. Instead, you can use two separate Task
s running in the same event loop, which is one of the main points of asynchronous programming:
import asyncio
async def countdown(n, m): # <- coroutine function
print("timer start")
await asyncio.sleep(n)
print("timer stop")
await coro1()
async def coro1():
print("coroutine called")
async def coromain():
print("first")
asyncio.ensure_future(countdown(5, 0)) # create a new Task
print("second")
loop = asyncio.get_event_loop()
loop.run_until_complete(coromain()) # run coromain() from sync code
pending = asyncio.Task.all_tasks() # get all pending tasks
loop.run_until_complete(asyncio.gather(*pending)) # wait for tasks to finish normally
Output:
first
second
timer start
(5 second wait)
timer stop
coroutine called
When using ensure_future
, you effectively make a new “execution thread” (see fibers) inside the single OS's thread.
Upvotes: 5
Reputation:
After some digging crafted this workaround. It might not be pretty, but it works:
import time, threading, asyncio
def countdown(n, m):
print("timer start")
time.sleep(n)
print("timer stop")
looptemp = asyncio.new_event_loop()
asyncio.set_event_loop(looptemp)
loop2 = asyncio.get_event_loop()
loop2.run_until_complete(coro1())
loop2.close()
async def coro1():
print("coroutine called")
async def coromain():
print("first")
t1 = threading.Thread(target=countdown, args=(5, 0))
t1.start()
print("second")
loop = asyncio.get_event_loop()
loop.run_until_complete(coromain())
loop.stop()
It unfortunately doesn't work for my specific usecase, but I thought it might be useful.
Upvotes: 0