我认为占用大量内存并且是异步的。我是否可以限制在处理程序功能内同时工作的连接数(如内部有N max个工作人员的关键部分)。
龙卷风有可能吗?
像:
@tornado.web.asynchronous
def get(self):
with critical_section(count=5):
# some code
由于
答案 0 :(得分:3)
Toro提供类似于Tornado协同程序的线程模块中的同步原语。您可以使用它的BoundedSemaphore来输入处理程序主体:
# global semaphore
sem = toro.BoundedSemaphore(5)
@gen.coroutine
def get(self):
with (yield sem.acquire()):
# do work
答案 1 :(得分:1)
简答:
据我所知,Tornado和其他使用Future / Deferred / generator并发的框架,这是不可能的。但是,绝对可以使用高阶函数,即critical_section()
辅助函数,它将with
- 块的主体作为参数。
答案很长:
据我所知,Tornado的并发性非常类似于Twisted;这意味着非阻止调用仅限于使用Future
和yield
(基于Twisted的@inlineCallbacks
或Tornado中的等效内容。)
为了实现critical_section
上下文管理器,它必须在内部与反应堆合作;这只能通过回调或yield
来实现。但是,它们都不能与上下文管理器组合。
在我记得这个之前,我实际上已经抛出了一些代码。这是我提出的代码:
import sys
from contextlib import contextmanager
from collections import defaultdict
from tornado.concurrent import Future
_critical_sections = defaultdict(lambda: (0, []))
@contextmanager
def critical_section(count):
# get the code location of the critical section
frame = sys._getframe()
orig_caller = frame.f_back.f_back
lineno = orig_caller.f_lineno
filename = orig_caller.f_code.co_filename
loc = (filename, lineno)
count, waiters = _critical_sections[loc]
if count > 5:
future = Future()
_critical_sections[loc] = (count + 1, waiters + [future])
# XXX: not possible; either have to set a callback or use yield, but
# then this context manager itself would not work as you'd expect:
future.wait() # <---- not possible in Tornado nor Twisted; only in Gevent/Eventlet
fn(*args, **kwargs)
else:
_critical_sections[loc] = (count + 1, waiters)
try:
yield
finally:
count, waiters = _critical_sections[loc]
_, w_future = waiters[0]
_critical_sections[loc] = (count, waiters[1:])
w_future.set_result(None)
(无论如何我还没有测试过,而且无论如何它都不能在Tornado上运行。)
现在,如果您对提议的方法感到满意,这里有一些东西可以帮助您入门(或者甚至可以开箱即用):
...
def _critical_section(count, fn, *args, **kwargs):
...
if count > 5:
future = Future()
future.add_done_callback(lambda _: fn(*args, **kwargs))
_critical_sections[loc] = (count + 1, waiters + [future])
# XXX: not possible; either have to set a callback or use yield, but
# then this context manager itself would not work as you'd expect:
return future
else:
_critical_sections[loc] = (count + 1, waiters)
try:
return fn()
finally:
... # same
然后你可以把它变成装饰者:
from functools import wraps
def critical_section(count):
def decorate(fn):
@wraps(fn)
def ret(*args, **kwargs):
return _critical_section(count, fn, *args, **kwargs)
return ret
return decorate
<强>用法:强>
@tornado.web.asynchronous
def get(self):
@critical_section(count=5)
def some_code():
pass # do stuff
此外,代码正在使用sys._getframe()
,其中包含(至少)2个含义:
sys._getframe
进行JIT编译功能),但大多数情况下,当涉及到Web时,这是一个可接受的权衡代码.pyc
并删除.py
,我认为代码不会起作用 - 它不应该能够确定调用代码的文件名和行号,所以它(可能)不能唯一地区分关键部分的位置,在这种情况下你必须使用锁定对象。注意:上下文管理器版本在Gevent(http://gevent.org)或Eventlet上完全可行。