Reputation: 43
I'm trying to add multiple callbacks in tornado main loop. But when I run this code:
def task(num):
print 'task %s' % num
if __name__ == '__main__':
for i in range(1,5):
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(num=i))
tornado.ioloop.IOLoop.instance().start()
I get output 5 times: 'task 5', not task 1.. task 5. When I change main like that:
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(1))
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(2))
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(3))
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(4))
everything works fine (I get task1-task5 in output). What am I doing wrong in the first case?
Upvotes: 3
Views: 7295
Reputation: 331
Wrap your lambda into functools.partial() function which solves "pointer issue"
IOLoop.instance().add_callback(callback=functools.partial(task, i*10))
tl;dr;
Here is an example with the "pointer issue" BUG:
def task(num):
print(num)
for i in range(1, 6):
print ("poop {0}".format(i))
IOLoop.instance().add_callback(callback=lambda: task(i))
IOLoop.instance().start()
at the code example above i
will be a memory pointer to the value range()
currently iterating on. It is changing from 1 to 2 then to 3, 4, 5.
Tornado's add_callback() asynchronous function returns control to the main program immediately making all the 5 calls to task()
function execute almost at the same time. And at this point i
is already got the value of 5
.
To solve this issue quickly use funtools.partial
function instead of lambda and research the difference ;)
Here is BUG-free example:
IOLoop.instance().add_callback(callback=functools.partial(task, i*10))
Upvotes: 1
Reputation: 1344
Maybe there is the same problem like in JS? look at this answer: JavaScript closure inside loops – simple practical example
"the problem is that the variable i, within each of your anonymous functions, is bound to the same variable outside of the function."
try simple test:
def task(num):
print 'task %s' % num
def create_task(num):
tornado.ioloop.IOLoop.instance().add_callback(callback=lambda: task(num))
if __name__ == '__main__':
for i in range(1,5):
create_task(i)
tornado.ioloop.IOLoop.instance().start()
Upvotes: 1