在threading module中,有一个非常特别的类local。一旦在主线程实例化了一个local,它会一直活在主线程中,并且又主线程启动的子线程调用这个local实例时,它的值将会保存在相应的子线程的字典中。
我们先看看测试代码:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Description: test the threading.local class
# Create: 2008-6-4
# Author: MK2[fengmk2@gmail.com]
from threading import local, enumerate, Thread, currentThread
local_data = local()
local_data.name = 'local_data'
class TestThread(Thread):
def run(self):
print currentThread()
print local_data.__dict__
local_data.name = self.getName()
local_data.add_by_sub_thread = self.getName()
print local_data.__dict__
if __name__ == '__main__':
print currentThread()
print local_data.__dict__
t1 = TestThread()
t1.start()
t1.join()
t2 = TestThread()
t2.start()
t2.join()
print currentThread()
print local_data.__dict__
运行结果:
<_MainThread(MainThread, started)>
{'name': 'local_data'}
<TestThread(Thread-1, started)>
{}
{'add_by_sub_thread': 'Thread-1', 'name': 'Thread-1'}
<TestThread(Thread-2, started)>
{}
{'add_by_sub_thread': 'Thread-2', 'name': 'Thread-2'}
<_MainThread(MainThread, started)>
{'name': 'local_data'}
主线程中的local_data并没有被改变,而子线程中的local_data各自都不相同。
怎么这么神奇?local_data具有全局访问权,主线程,子线程都能访问它,但是它的值却是各当前线程有关,究竟什么奥秘在这里呢?
查看了一下local的源代码,发现就神奇在_path()方法中:
def _patch(self):
key = object.__getattribute__(self, '_local__key')
d = currentThread().__dict__.get(key)
if d is None:
d = {}
currentThread().__dict__[key] = d
object.__setattr__(self, '__dict__', d)
# we have a new instance dict, so call out __init__ if we have
# one
cls = type(self)
if cls.__init__ is not object.__init__:
args, kw = object.__getattribute__(self, '_local__args')
cls.__init__(self, *args, **kw)
else:
object.__setattr__(self, '__dict__', d)
每次调用local实例的属性前,local都会调用这个方法,找到它保存值的地方.
d = currentThread().__dict__.get(key) 就是这个地方,确定了local_data值的保存位置。所以子线程访问local_data时,并不是获取主线程的local_data的值,在子线程第一次访问它是,它是一个空白的字典对象,所以local_data.__dict__为 {},就像我们的输出结果一样。
如果想在当前线程保存一个全局值,并且各自线程互不干扰,使用local类吧。
先来看看我们之前是怎样获取到当前登录user的:在view中,我们常常就会通过request对象来获取当前用户user的引用:
def comment_add(request):
# do something...
user = request.user
# to do .....
这样,确实很方便就能获取多用户的信息。可是,如果要做别的地方获取user呢?例如要在model中获取user呢?
class Post(models.Model):
title = models.CharField('Post标题', maxlength=100, db_index=True)
create_date = models.DateTimeField('发布时间', default=datetime.now(), editable=False)
modified_date = models.DateTimeField('最后修改时间', default=datetime.now(), editable=False)
author = models.ForeignKey(User, verbose_name="作者", editable=False)
content = models.TextField('内容')
last_visitor_IP = models.IPAddressField('最后的访问者IP', null=True, blank=True, editable=False)
tags = models.ManyToManyField(Tag, verbose_name="the list of tags", null=True, blank=True)
slug = models.SlugField('自定义url', maxlength=100, null=True, blank=True)
hits = models.IntegerField('点击数', editable=False, default=0)
可以看到,author是editable=False的,即我们想Post被保存是自动设置author为当前登录用户user。而这里没有request,怎样获取到这个user呢?找了一下django的文章,提到了使用middleware来解决这个需求。
好吧,先在项目中创建一个"middleware"模块(即带有一个空__init__.py文件的目录),接着创建"threadlocals.py"到此目录,代码如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Description: thread locals middleware
# Create: 2008-6-4
try:
from threading import local
except ImportError:
from django.utils._threading_local import local
_thread_locals = local()
def get_current_user():
return getattr(_thread_locals, 'user', None)
class ThreadLocals(object):
"""Middleware that gets various objects from the
request object and saves them in thread local storage."""
def process_request(self, request):
_thread_locals.user = getattr(request, 'user', None)
接着将此middleware添加到项目中,打开setting.py,修改代码如下:
MIDDLEWARE_CLASSES = (
"django.middleware.common.CommonMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"yourproject.middleware.threadlocals.ThreadLocals",
)
写到这里,发现这不类似与asp.net中的HttpModule吗?
然后为了自动设置author为当前用户,我们需要重载post的save方法:
先导入middleware
from net4.middleware import threadlocals
def save(self):
if self.id is None:
self.author = threadlocals.get_current_user()
super(Post, self).save()
编码工作完成了,还不快点进入admin看看是否可以正常工作了呢?
其实大部分都是算直接在参考文章里哪过来的,呵呵,反正是花了点时间才找到,呵呵,就拿来主义,让大家一齐分享咯。
希望对你有用! ^_^
本文参考:
Making User info available outside requests