Flask:local

threading.local

多线程下共享数据的两个方式:管道和共享变量,共享变量需要加锁(防止数据写乱)。threading.loacl类,以每条线程的ID号为字典的key,对应val(数据),在不加锁的情况下,能保证多个线程修改同一数据,复制多份变量给每个线程用,为每个线程开辟一块空间进行数据存储。

进程和线程有ID号,协程没有,flask重写了threading.loacl,也支持协程。因此flask框架用的全局request,每来一个请求启一条线程,不同线程用的是自己当前线程的request,所以数据不会乱。


不用threading.local

10条线程正常应该打印0-9, 但中间阻塞了2秒,直到数据改为9,全部打印的9,数据乱了

from threading import Thread
import time

lqz = -1

def task(arg):
    global lqz
    lqz = arg
    time.sleep(2)
    print(lqz)

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

使用threading.local

lqz是local对象,它以线程id为key存储数据{'线程id':{value: 0}, '线程id':{value: 1}, '线程id':{value: 2}......},在自己线程内打印拿到的就是自己的value,没有加锁,数据也不会乱

from threading import Thread
from threading import local
import time

# 特殊对象, local实例化得到对象
lqz = local()

def task(arg):
    # 对象.val = 1/2/3/4/5
    lqz.value = arg
    time.sleep(2)
    print(lqz.value)

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

通过字典自定义threading.local(函数版)

from threading import Thread, get_ident
import time

storage = {}

def set(k ,v):
    # 按照 {'线程id':{val: 0}, '线程id':{val: 1}, '线程id':{val: 2}...} 构造数据
    ident = get_ident()  # 获取当前线程id号
    if ident in storage:
        storage[ident][k] = v
    else:
        storage[ident] = {k: v}

def get(k):
    ident = get_ident()
    return storage[ident][k]

def task(arg):
    set('val', arg)  # 存值
    time.sleep(2)
    v = get('val')   # 取值
    print(v)

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

通过字典自定义threading.local(面向对象版)

from threading import Thread, get_ident
import time

class Local(object):
    storage = {}

    def set(self, k, v):
        ident = get_ident()
        if ident in Local.storage:
            Local.storage[ident][k] = v
        else:
            Local.storage[ident] = {k: v}

    def get(self, k):
        ident = get_ident()
        return Local.storage[ident][k]


obj = Local()

def task(arg):
    obj.set('val', arg)
    time.sleep(2)
    v = obj.get('val')
    print(v)

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

通过字典自定义threading.local(接近local)

通过setattr和getattr实现,有一个问题:storage是类属性,生成obj1和obj2共用一个名称空间,即共用一个字典(storage)

from threading import Thread, get_ident
import time

class Local(object):
    storage = {}

    def __setattr__(self, k, v):
        ident = get_ident()
        if ident in Local.storage:
            Local.storage[ident][k] = v
        else:
            Local.storage[ident] = {k: v}

    def __getattr__(self, key):
        ident = get_ident()
        return Local.storage[ident][k]


obj = Local()

def task(arg):
    obj.val = arg
    time.sleep(2)
    print(obj.val)

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

通过字典自定义threading.local(终极版)

把storage放到__init__中,每次实例化时初始化得到一个字典,每次实例化得到local对象,用自己的字典存储

from threading import Thread, get_ident
import time

class Local(object):
    def __init__(self):
        """
        不能以self.storage={} 方式给storage赋值
        当obj.val=arg赋值时,自动触发__setattr__方法,方法中调用self.storage,又触发__setattr__,形成无限递归调用
        """
        # self.storage = {}
        """
        object类调也有__setattr__方法,类调用绑定给对象的方法,就是调用普通函数, 有几个参数传几个
        Local初始化时,执行下面代码,把key和value放到对象中,相当于self.storage = {},给对象赋值
        """
        object.__setattr__(self, 'storage', {})

    def __setattr__(self, k, v):
        ident = get_ident()
        if ident in self.storage:
            self.storage[ident][k] = v
        else:
            self.storage[ident] = {k: v}

    def __getattr__(self, k):
        ident = get_ident()
        return self.storage[ident][k]


obj = Local()

def task(arg):
    obj.val = arg
    time.sleep(2)
    print(obj.val)

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

通过字典自定义threading.local(支持协程)

终极版用的是线程id号,不支持协程,协程是单线程下实现并发,每一个线程id号对应多个协程,需要拿到协程id号

"""
getcurrent获取协程id,重命名为get_ident
如果开了协程,get_ident拿到的就是协程id,否则拿到的就是线程id
"""

try:
    from greenlet import getcurrent as get_ident  
except Exception as e:
    from threading import get_ident
from threading import Thread
import time

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

    def __setattr__(self, k, v):
        ident = get_ident()
        if ident in self.storage:
            self.storage[ident][k] = v
        else:
            self.storage[ident] = {k: v}

    def __getattr__(self, k):
        ident = get_ident()
        return self.storage[ident][k]


obj = Local()

def task(arg):
    obj.val = arg
    time.sleep(2)
    print(obj.val)

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

 

posted @ 2022-10-08 13:33  不会钓鱼的猫  阅读(39)  评论(0编辑  收藏  举报