Flask实现线程隔离

  1. 什么是线程隔离

简单的说,就是将用户请求线程和服务执行线程分割开来,同时约定了每个服务最多可用线程数。

1.Local(线程隔离对象)

class Local(object):
    __slots__ = ("__storage__", "__ident_func__")

    def __init__(self):
        object.__setattr__(self, "__storage__", {})
        object.__setattr__(self, "__ident_func__", get_ident)

    def __iter__(self):
        return iter(self.__storage__.items())

    def __call__(self, proxy):
        """Create a proxy for a name."""
        return LocalProxy(self, proxy)

    def __release_local__(self):
        self.__storage__.pop(self.__ident_func__(), None)

    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

    def __delattr__(self, name):
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

Local 是werkzeug.local下的一个类,也是实现线程隔离的核心。

简述一下local类会干什么

  1. 首先在实例化的时候回创建一个字典,格式为
storage={}
  1. 接着看到_setattr__方法中,每次在设置值的时候,会获取当前线程的ID,要存的k-v
  2. _setattr__成功了一个,结构会变成
storage={
	ID(当前线程id):{name(k):value(v)}
		}
  1. 如此storage会变成一个双层的字典,第一层字典会以线程ID作为key值,如此一来,每个线程都会拥有自己的一个数据空间(storage的第二层字典)。Local就是通过双层字典的方式实现的线程隔离。

Local_test

import threading
import time

from werkzeug.local import LocalStack,Local
obj_local = Local()
obj_local.a = 1

def modiftData():
    obj_local.a = 2

    print('new thread a: {}'.format(obj_local.a))


if __name__ == '__main__':
    # 新建一个子线程执行
    new_thread = threading.Thread(target=modiftData)
    new_thread.start()

    time.sleep(2)
    print('main thread a: {}'.format(obj_local.a))

在这里插入图片描述
结果验证确实做到了线程隔离的作用,不过这样看有点不够,不明白到底是怎么运行的,下面就详细讲解它的过程。

  • 主线程赋值过程
    在这里插入图片描述
    如图,将线程Id作为了key值存到了storage里,并且将a=1存进了线程key里。
    进入异常是因为字典赋值的时候,想寻找到第二个key是,不能找到第一个key的位置。(不是重点)
  • 新线程赋值
    在这里插入图片描述
    赋值流程与主线程是一样的,不过最后可以看到,storage是同时存了两个线程的。也验证了使用storage作为双层字典,ID(线程)为第一层的key实现了线程隔离。
    ps:不要随便改源码,出了问题很麻烦,这里为了展示添加的占位符

2.LocalStack(线程隔离栈)

LocalStack实现线程隔离,是通过对Local进行封装。主要是内部实例化了一个Local对象,这是LocalStack的核心

源码太长,给个图好了
在这里插入图片描述

然后为了实现栈应有的功能,定义了两个方法(push,pop)和一个属性(top)
在这里插入图片描述

storage={
	ID:{
		'stack':[]
	}
}

LocalStack的数据结构本质就是在local二级字典里,添加了一个key为stack,value为List的k-v。
然后对外只提供对stack的append和pop方法,让它实现栈应有的功能。
在这里插入图片描述

3.为什么要使用线程隔离

在这里插入图片描述

  • 如图是访问了两次的/hello的api,打印出的不同的结果,可以看出no_local这个类的实例,两次打出的数据是不一样的。造成的原因是我定义的这个类本身并没有做线程隔离的操作。
  • Flask里请求会有一个Request的类来生成一个实例,被request请求上下文封装,然后request又被LocalStack存储。所以实现了线程隔离。
  • 如此,线程隔离可以确保每次请求的数据都是一样的,不会因为请求的先后造成数据污染。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值