李迎辉

李迎辉的博客

他的个人主页  他的博客

Uliweb中func_globals共享问题的修

李迎辉  2009年08月06日 星期四 14:11 | 1231次浏览 | 2条评论

今天在uliweb的邮件列表,shuhanwu提了一个问题:在多线程下,request对象不是线程安全的。并且给出了试例及可能出错代码的位置。于是我研究了一下,发现的确有这个问题。

问题原因

这个问题的产生与uliweb对view函数的设计造成的。uliweb在处理view函数时,使用了一种hack的机制。每个view方法都有一个func_globals的属性,它用来记录可以在函数中直接访问的全局对象的信息。因此uliweb在调用每个view方法之前,会将一些变量,如:request, response等信息添加到这个属性中,这样你就可以在view中不需要定义这些变量而直接使用了。但是这个func_globals属性对于每个函数来说是一份实例,因此对于不同的view函数,func_globals仍然是同一个对象。所以尽管我希望每个view函数的func_globals在不同线程下是不同的,但实际上是做不到的。于是造成后执行的view方法会将其它函数中的request,response等对象覆盖。

解决方案

知道问题产生的原因。我使用了以下的方案。

1. 使用threading.local来保存线程安全的request, response对象。在werkzeug中已经提供了Local类。而且在SimpleFrame.py中已经有一个全局的local对象可以直接使用。因此在__call__()中会将创建的request, response对象首先保存到local中去。__call__()是处理每个view请求的一个调度程序。

2. 在__call__()中最终会调用一个_call_function()的函数,它将完成向func_globals中添加新的属性的功能。因为我仍然希望用户不需要定义request, response的方式来处理view方法,因此向func_globals注入对象的方式仍然会使用。那么就要在request, response本身进行改造。这里我创建了两个新的RequestProxy和ResponseProxy的类,所以对request和response的属性操作,如request.GET这种操作,都将转换为对local.request或local.response的操作,包括给属性赋值。示例代码如下:

class RequestProxy(object):

    def __init__(self, req):

        local.request = req

    def instance(self):

        return local.request

    def __getattr__(self, name):

        return getattr(local.request, name)

    def __setattr__(self, name, value):

        setattr(local.request, name, value)

这样,在用户的view中实际使用的request和response其实是RequestProxy和ResponseProxy类,操作的也是线程安全的request, response对象,但是用户可以认为是等同的。

经过简单的一些测试应该是可用的。






评论

我的评论:

发表评论

请 登录 后发表评论。还没有在Zeuux哲思注册吗?现在 注册 !
李迎辉

回复 李迎辉  2009年08月06日 星期四 22:39

是。threading.local是python解决线程安全的一种做法,它是将不同线程的变量分开了,而不是通过锁对同一个对象的访问,有自已的应用场景。这里的问题主要是我采用的hack机制本身是共享对象,只能通过其它的方式来实现对threading.local的访问。

0条回复

夏清然

回复 夏清然  2009年08月06日 星期四 22:34

这样看使用threading.local来保存线程内使用的变量是一个解决方法。

0条回复

暂时没有评论

Zeuux © 2024

京ICP备05028076号