flask请求上下文源码解析

预热

面向对象

class Local(object):
    def __init__(self):
        self.dict = {}

    def __setattr__(self, key, value):
        self.dict[key] = value

obj = Local()

这样写会报错

如果自定义了自己的__setattr__,在初始化的时候就不能这样给对象绑定属性,因为使用self.dict = {}的时候就会去调用__setattr__去给对象设置属性,但是此时对象还没有一个dict的属性。但凡是给自己写的
这个类实例化的对象,调用对象的obj.xx = oo 这种方式都会触发__setattr__的执行,但是如果在这个对象里操作一个字典对象(或其他对象),对这个字典对象(或其他对象)的操作就不会有问题,因为就算其他对象的.方式
是调用其他对象自己的__setattr__,和我们写的类没有关系

ThreadLocal

from threading import get_ident
from threading import Thread
import time
class Local(object):
    pass

obj = Local()

def task(i):
    obj.x  = i
    time.sleep(1)
    print(get_ident(),':',obj.x)

if __name__ == '__main__':
    for i in range(10):
        t = Thread(target=task, args=(i,))
        t.start()

得到的结果如下

这是因为在python中的多线程中,对同一个全局变量的修改会有竞争问题,此时全局变量obj最终存的值是最后一个线程赋的值。要想实现每个线程的数据隔离,单独的线程打印单独线程传入的变量,可以在每个线程维护相对应的局部变量,代码如下

from threading import get_ident
from threading import Thread
import time
class Local(object):
    pass

obj = Local()

def task(i):
    dic = {'i':i}
    time.sleep(1)
    print(get_ident(),':',dic['i'])

if __name__ == '__main__':
    for i in range(10):
        t = Thread(target=task, args=(i,))
        t.start()

此时的结果为

但是python中还提供了一个叫做线程局部变量的玩意,叫local.

from threading import local
from threading import get_ident
from threading import Thread
import time
obj = local()

def task(i):
    obj.x  = i
    time.sleep(1)
    print(get_ident(),':',obj.x)

if __name__ == '__main__':
    for i in range(10):
        t = Thread(target=task, args=(i,))
        t.start()

此时的结果就是

从上面的结果不难看出,为什么local叫线程局部变量,因为local对象明明是定义在全局的,但是却有类似于在线程创建相应的局部变量的效果。
从使用效果上来看,这个叫做线程局部变量的玩意好牛逼,能让我们以很简单类似于平常使用类的方式做到了数据隔离。是的,乍一看很牛逼,说白了就是别人在内部做了一些事,封装的好。那么内部做了些啥事呢,可以这样先简单地去理解:这个对象内部维护一个大字典,字典的key就是线程的唯一标识,value也是一个字典,这个字典存线程的数据。通过这个想法,自定义一个支持协程的隔离数据的类

try:
    from gevent import getcurrent as get_ident
except ImportError:
    from threading import get_ident
from threading import Thread
import time


class Local(object):
    def __init__(self):
        object.__setattr__(self,'__storage__', {})

    def __setattr__(self, key, value):
        thread_id = get_ident()
        try:
            self.__storage__[thread_id][key] = value
        except KeyError as e:
            self.__storage__[thread_id] = {key: value}

    def __getattr__(self, item):
        # __getattr__ 是在obj.属性的时候被调用,而且这个属性必须不是对象本身的属性
        # 这里__setattr__ 并不是把属性绑定到对象,而是放到字典里,所以即使obj.x = 1,访问obj.x 还是会调用__getattr__
        thread_id = get_ident()
        try:
            return self.__storage__[thread_id][item]
        except KeyError:
            return None
obj = Local()
def task(i):
    obj.x  = i
    time.sleep(1)
    print(get_ident(),':',obj.x)

if __name__ == '__main__':
    for i in range(10):
        t = Thread(target=task, args=(i,))
        t.start()

猜你喜欢

转载自www.cnblogs.com/longyunfeigu/p/9466168.html
今日推荐