在 Tornado 中,如果在子线程中运行一个耗时操作(例如进行数据查询或文件读写),可能会导致主线程阻塞,从而无法处理其他请求。这是因为 Tornado 使用的是单线程模型,所有的请求都由主线程来处理。如果主线程被阻塞,那么其他请求就无法得到处理。
2、解决方案
为了避免主线程阻塞,我们可以使用以下几种方法:
- 方法一:使用
ThreadPoolExecutor
ThreadPoolExecutor
是 Python 内置的线程池库,我们可以使用它来创建多个线程,并将耗时操作分配给这些线程来执行。这样,主线程就可以继续处理其他请求,而不会被阻塞。
from concurrent.futures import ThreadPoolExecutor
def long_blocking_function(index, sleep_time):
print ("Entering run counter:%s" % (index,))
time.sleep(sleep_time)
print ("Exiting run counter:%s" % (index,))
return "Result from %d" % index
class FooHandler(tornado.web.RequestHandler):
executor = ThreadPoolExecutor(max_workers=10)
@tornado.gen.coroutine
def get(self):
global counter
counter += 1
current_counter = str(counter)
print ("ABOUT to spawn thread for counter:%s" % (current_counter,))
result = yield self.executor.submit(long_blocking_function,
index=current_counter,
sleep_time=5)
self.write(result)
print ("DONE with the long function")
- 方法二:使用
AsyncHTTPClient
AsyncHTTPClient
是 Tornado 内置的异步 HTTP 客户端库,我们可以使用它来进行网络请求。这样,网络请求就可以在子线程中执行,而不会阻塞主线程。
import tornado.httpclient
def long_blocking_function(index, sleep_time):
print ("Entering run counter:%s" % (index,))
time.sleep(sleep_time)
print ("Exiting run counter:%s" % (index,))
return "Result from %d" % index
class FooHandler(tornado.web.RequestHandler):
http_client = tornado.httpclient.AsyncHTTPClient()
@tornado.gen.coroutine
def get(self):
global counter
counter += 1
current_counter = str(counter)
print ("ABOUT to spawn thread for counter:%s" % (current_counter,))
response = yield self.http_client.fetch("http://example.com")
self.write(response.body)
print ("DONE with the long function")
- 方法三:使用
asyncio
asyncio
是 Python 内置的异步 I/O 库,我们可以使用它来编写异步代码。这样,我们可以将耗时操作包装成一个协程,并在子线程中执行它,而不会阻塞主线程。
import asyncio
def long_blocking_function(index, sleep_time):
print ("Entering run counter:%s" % (index,))
yield asyncio.sleep(sleep_time)
print ("Exiting run counter:%s" % (index,))
return "Result from %d" % index
class FooHandler(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
global counter
counter += 1
current_counter = str(counter)
print ("ABOUT to spawn thread for counter:%s" % (current_counter,))
result = yield long_blocking_function(current_counter, 5)
self.write(result)
print ("DONE with the long function")