05. flask的 threading.local

本文探讨了在Flask应用中,如何通过使用`threading.local`解决并发问题,包括原始全局变量的并发问题、利用内置的local对象确保线程隔离,以及自定义低配和面向对象的local实现。作者展示了如何通过这些方法避免数据竞争,提高线程安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

flask的 threading.local

(1)不用local对象,会出现并发安全的问题

from threading import Thread
import time
num = -1
def task(arg):
    global num
    num = arg
    time.sleep(1)
    print(num)

for i in range(10):
    t = Thread(target=task, args=(i,))
    t.start()
# 每次打印结果都是9

(2)使用local

# local是种特殊的对象,不同线程操作它,操作的是线程自己的,不会出现并发安全问题
from threading import Thread
from threading import local
import time

num = local()
def task(arg):
    num.value = arg
    time.sleep(1)
    print(num.value)

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

(3)自己写个低配local对象

from threading import get_ident, Thread
import time

storage = {} # storage的格式为{线程id号1:{k:v}, 线程id号2:{k:v}, ...}

def set(k, v):
    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(1)
    v = get('val')
    print(v)

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

(4) 用面向对象重写local

from threading import get_ident, Thread
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(1)
    v = obj.get('val')
    print(v)

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

(5) 面向对象版本,通过getattr取值、setattr赋值

from threading import get_ident, Thread
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, k):
        ident = get_ident()
        return Local.storage[ident][k]

obj = Local()

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

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

# 缺陷在于:每个线程实例化得到的都是同一个storage字典

(6)改良版本(不同的local对象使用自己的字典)

try:
    from greenlet import getcurrent as get_ident
except Exception as e:
    from threading import get_ident
# 加上上面的代码就可以处理多协程的并发安全问题,getcurrent方法是取协程号的    
from threading import Thread
import time
class Local(object):
    def __init__(self):
        # self.storage = {}  
        # setattr(self, 'storage', {}) 
        # Local.__setattr__(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(1)
    print(obj.val)

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值