阅读完关于线程安全的这份文档后,我感觉文档中似乎有些东西缺失了,或者是我的理解或推理出现了问题。
我们来举一个简单的例子:
class HelloWorldNode(template.Node):
def render(self, context):
return "O HAI LOL"
@register.tag(name="hello_world")
def hello_world(parser, tokens):
"""
Greets the world with wide-eyed awe.
"""
return HelloWorldNode()
我理解这段代码是在hello_world
标签被使用时构造一个HelloWorldNode
类的新实例。其他例子包括向构造函数传递参数,例如:
class HelloWorldNode(template.Node):
def __init__(self, message):
self.message = message
def render(self, context):
return "O HAI LOL " + message
@register.tag(name="hello_world")
def hello_world(parser, tokens):
"""
Greets the world with wide-eyed awe.
"""
message = tokens.split_contents()[1]
return HelloWorldNode(message)
因此,当执行
hello_world
时,将创建一个新的HelloWorldNode实例,并且该实例字典具有一个属性message
。这个实例必须仅用于呈现给定标记实例,因为将其用于其他呈现将意味着绑定到它的数据将不正确。如果这不是这种情况,那么参数将在不同用途的标记之间混淆。从文档中查看其他示例,以下是来自此处的简化示例:
def do_current_time(parser, token):
tag_name, format_string = token.split_contents()
return CurrentTimeNode(format_string[1:-1])
由于当前时间节点(CurrentTimeNode)从传递给该函数的令牌中获取数据,因此唯一能使其工作的方法是在每次调用“do_current_time”时实例化一个新的时间节点。
回到文档页面,这里存在不协调之处。这是"糟糕的"。
class CycleNode(Node):
def __init__(self, cyclevars):
self.cycle_iter = itertools.cycle(cyclevars)
def render(self, context):
return self.cycle_iter.next()
文档指出,如果两个页面使用相同的标签并且同时使用同一个节点,则可能会出现竞争条件。但是,如果它们都独立实例化自己的节点,我不明白两个模板的渲染如何最终共享同一实例。
文档中提到解决此问题的方法如下:
class CycleNode(Node):
def __init__(self, cyclevars):
self.cyclevars = cyclevars
def render(self, context):
if self not in context.render_context:
context.render_context[self] = itertools.cycle(self.cyclevars)
cycle_iter = context.render_context[self]
return cycle_iter.next()
这似乎是使用self
来索引context.render_context
。这意味着self
被用来以以下两种方式之一标识实例:
self
在整个系统中引用类的某个特定实例self
仅引用该类,并且为了引用实例,需要一个渲染上下文
如果1成立,为什么不只是将数据与self
关联?
如果2成立,且渲染上下文“与当前正在渲染的模板的上下文相关联”,如何区分同一页上两个模板标记的两个实例?
每次调用标记时,Node是否单独实例化?如果是,为什么会有并发问题?如果不是,为什么不是?