Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Tornado - Confused how to convert a blocking function into a non-blocking function

Tags:

python

tornado

Suppose I have a long running function:

def long_running_function():
    result_future = Future()
    result = 0
    for i in xrange(500000):
        result += i
    result_future.set_result(result)
    return result_future

I have a get function in a handler that prints the user with the above result of a for loop that adds all the number in the xrange:

@gen.coroutine
def get(self):
    print "start"

    self.future = long_running_function()
    message = yield self.future
    self.write(str(message))

    print "end"

If I run the above code on two web browsers simultaneously, I get:

start

end

start

end

Which seems to be blocking. From my understanding, the @gen.coroutine and the yield statement does not block the IOLoop in the get function, however, if any functions that is inside the co-routine that is blocking, then it blocks the IOLoop.

Hence the other thing I did is to turn the long_running_function into a callback, and using the yield gen.Task instead.

@gen.coroutine
def get(self):
    print "start"

    self.future = self.long_running_function
    message = yield gen.Task(self.future, None)
    self.write(str(message))

    print "end"

def long_running_function(self, arguments, callback):
    result = 0
    for i in xrange(50000000):
        result += i
    return callback(result)

This doesn't cut too, it gives me:

start

end

start

end

I can use threads to execute those in parallel, but it doesn't seem the way to go, because I might be opening a lot of threads, and according to Tornado's user guide, it may be expensive.

How do people write async libraries for Tornado?

like image 383
user1157751 Avatar asked Aug 21 '15 20:08

user1157751


1 Answers

If the blocking function is CPU-bound (as your for/xrange example is), then threads (or processes) are the only way to make it non-blocking. Creating a thread per incoming request is expensive, but making a small ThreadPoolExecutor to handle all CPU-bound operations is not.

To make a function non-blocking without using threads, the function must be event-driven: it must be waiting on some external event (such as network I/O) so that it can be awoken when that event occurs.

like image 110
Ben Darnell Avatar answered Sep 28 '22 01:09

Ben Darnell