I'm following the Tornado tutorial here: http://www.tornadoweb.org/en/stable/locks.html#tornado.locks.Condition
Now instead of one waiter, I want two threads waiting for the same condition variable. When the condition is released, I want the threads to start doing work asynchronously.
Here's my attempt to do this:
from time import sleep
from tornado import gen
from tornado.ioloop import IOLoop
from tornado.locks import Condition
condition = Condition()
@gen.coroutine
def waiter():
print('1: waiting')
yield condition.wait() # Yield a Future.
print('1: finish waiting')
for i in range(5):
sleep(1)
print('1: doing work', i)
@gen.coroutine
def waiter2():
print('2: waiting')
yield condition.wait() # Yield a Future.
print('2: finish waiting')
for i in range(5):
sleep(1)
print('2: doing work', i)
@gen.coroutine
def notifier():
print("About to notify")
sleep(2)
condition.notify_all()
print("Done notifying")
@gen.coroutine
def runner():
# combines the futures
yield [waiter(), waiter2(), notifier()]
IOLoop.current().run_sync(runner)
However, waiter2 always wakes up and starts doing work after waiter1 exits.
I have also attempted to call IOLoop.current().run_sync()
twice, but Tornado throws RuntimeError: IOLoop has already started.
Could anyone please show me what's the correct code to launch threads asynchronously? Thanks!
EDIT
The answer points out that sleep
should be replaced by gen.sleep
. This is completely correct for the code snippet I posted, so thank you for that.
However, the sleep()
here is only for illustration purpose. What I really want is waiter()
and waiter2()
doing long-running blocking processing simultaneously. How can I achieve that?
You cannot call time.sleep
in your code because such a long blocking call interferes fundamentally with the way tornado's IOLoop
works. Replace your calls to time.sleep
with the tornado.gen.sleep
coroutine and your code works just fine:
from time import sleep
from tornado import gen
from tornado.ioloop import IOLoop
from tornado.locks import Condition
condition = Condition()
@gen.coroutine
def waiter():
print('1: waiting')
yield condition.wait() # Yield a Future.
print('1: finish waiting')
for i in range(5):
yield gen.sleep(1)
print('1: doing work', i)
@gen.coroutine
def waiter2():
print('2: waiting')
yield condition.wait() # Yield a Future.
print('2: finish waiting')
for i in range(5):
yield gen.sleep(1)
print('2: doing work', i)
@gen.coroutine
def notifier():
print("About to notify")
yield gen.sleep(2)
condition.notify_all()
print("Done notifying")
@gen.coroutine
def runner():
# combines the futures
yield [waiter(), waiter2(), notifier()]
IOLoop.current().run_sync(runner)