Add an FAQ page.
This commit is contained in:
parent
b06e7de206
commit
83a0691723
|
@ -0,0 +1,96 @@
|
|||
Frequently Asked Questions
|
||||
==========================
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Why isn't this example with ``time.sleep()`` running in parallel?
|
||||
-----------------------------------------------------------------
|
||||
|
||||
Many people's first foray into Tornado's concurrency looks something like
|
||||
this::
|
||||
|
||||
class BadExampleHandler(RequestHandler):
|
||||
def get(self):
|
||||
for i in range(5):
|
||||
print(i)
|
||||
time.sleep(1)
|
||||
|
||||
Fetch this handler twice at the same time and you'll see that the second
|
||||
five-second countdown doesn't start until the first one has completely
|
||||
finished. The reason for this is that `time.sleep` is a **blocking**
|
||||
function: it doesn't allow control to return to the `.IOLoop` so that other
|
||||
handlers can be run.
|
||||
|
||||
Of course, `time.sleep` is really just a placeholder in these examples,
|
||||
the point is to show what happens when something in a handler gets slow.
|
||||
No matter what the real code is doing, to achieve concurrency blocking
|
||||
code must be replaced with non-blocking equivalents. This means one of three things:
|
||||
|
||||
1. *Find a coroutine-friendly equivalent.* For `time.sleep`, use
|
||||
`tornado.gen.sleep` instead::
|
||||
|
||||
class CoroutineSleepHandler(RequestHandler):
|
||||
@gen.coroutine
|
||||
def get(self):
|
||||
for i in range(5):
|
||||
print(i)
|
||||
yield gen.sleep(1)
|
||||
|
||||
When this option is available, it is usually the best approach.
|
||||
See the `Tornado wiki <https://github.com/tornadoweb/tornado/wiki/Links>`_
|
||||
for links to asynchronous libraries that may be useful.
|
||||
|
||||
2. *Find a callback-based equivalent.* Similar to the first option,
|
||||
callback-based libraries are available for many tasks, although they
|
||||
are slightly more complicated to use than a library designed for
|
||||
coroutines. These are typically used with `tornado.gen.Task` as an
|
||||
adapter::
|
||||
|
||||
class CoroutineTimeoutHandler(RequestHandler):
|
||||
@gen.coroutine
|
||||
def get(self):
|
||||
io_loop = IOLoop.current()
|
||||
for i in range(5):
|
||||
print(i)
|
||||
yield gen.Task(io_loop.add_timeout, io_loop.time() + 1)
|
||||
|
||||
Again, the
|
||||
`Tornado wiki <https://github.com/tornadoweb/tornado/wiki/Links>`_
|
||||
can be useful to find suitable libraries.
|
||||
|
||||
3. *Run the blocking code on another thread.* When asynchronous libraries
|
||||
are not available, `concurrent.futures.ThreadPoolExecutor` can be used
|
||||
to run any blocking code on another thread. This is a universal solution
|
||||
that can be used for any blocking function whether an asynchronous
|
||||
counterpart exists or not::
|
||||
|
||||
executor = concurrent.futures.ThreadPoolExecutor(8)
|
||||
|
||||
class ThreadPoolHandler(RequestHandler):
|
||||
@gen.coroutine
|
||||
def get(self):
|
||||
for i in range(5):
|
||||
print(i)
|
||||
yield executor.submit(time.sleep, 1)
|
||||
|
||||
See the :doc:`Asynchronous I/O <guide/async>` chapter of the Tornado
|
||||
user's guide for more on blocking and asynchronous functions.
|
||||
|
||||
|
||||
My code is asynchronous, but it's not running in parallel in two browser tabs.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Even when a handler is asynchronous and non-blocking, it can be surprisingly
|
||||
tricky to verify this. Browsers will recognize that you are trying to
|
||||
load the same page in two different tabs and delay the second request
|
||||
until the first has finished. To work around this and see that the server
|
||||
is in fact working in parallel, do one of two things:
|
||||
|
||||
* Add something to your urls to make them unique. Instead of
|
||||
``http://localhost:8888`` in both tabs, load
|
||||
``http://localhost:8888/?x=1`` in one and
|
||||
``http://localhost:8888/?x=2`` in the other.
|
||||
|
||||
* Use two different browsers. For example, Firefox will be able to load
|
||||
a url even while that same url is being loaded in a Chrome tab.
|
|
@ -129,6 +129,7 @@ This documentation is also available in `PDF and Epub formats
|
|||
coroutine
|
||||
integration
|
||||
utilities
|
||||
faq
|
||||
releases
|
||||
|
||||
* :ref:`genindex`
|
||||
|
|
Loading…
Reference in New Issue