python中的threading.local详解

1. 不使用threading.local之前

由于多线程共享全局变量,所以一个线程拿到的全局变量的值未必是当时自己修改后的值,有可能在拿到全局变量之前其它线程也对该全局变量进行了修改,原因是线程的调度是由操作系统决定的,如下:

from threading import Thread, get_ident
import time

num = 0


def task(arg):
    # get_ident()返回的就是每一个线程的唯一标识
    print(get_ident())
    global num
    num += 1
    time.sleep(1)
    print(num)


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

 由于每个线程只对全局变量做了一次修改,我们中间设置了1秒的延时,所以10个线程打印的全局变量的值都是最后修改的值,而并不是当时线程自己修改后的值。

2.  threading.local的基本使用

from threading import Thread, local, get_ident
import time


local_obj = local()


def task(arg):
    """
    threading.local的作用:为每个线程开辟一块空间进行数据的存储
    空间与空间之间数据是隔离的
    """
    # get_ident()返回的就是每一个线程的唯一标识
    print(get_ident())
    # 线程执行到此的时候,为每一个线程开辟一块空间用来存储对象的值
    local_obj.value = arg
    time.sleep(1)
    print(local_obj.value)


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

由于创建了threading.local对象,当为对象赋值属性的时候,会为每个线程开辟一块空间来存储对象的属性值,空间与空间之间数据是隔离的,这样最终打印的时候就是线程自己空间中保存的数据的值。

3. 自定义local对象,基于函数

import time
from threading import get_ident, Thread

storage = {}


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


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

4.  自定义local对象,基于面向对象:

import time
from threading import Thread, get_ident


class CustomLocal(object):
    """自定义local对象,基于面向对象"""
    def __init__(self):
        # self.storage = {}  # 执行此句代码的时候会先触发__setattr__方法
        # 为了避免报错:RecursionError: maximum recursion depth exceeded while calling a Python object
        # 需要先把storage创建出来,所以调用父类的__setattr__方法
        super(CustomLocal, self).__setattr__("storage", {})

    def __setattr__(self, key, value):
        ident = get_ident()
        if ident in self.storage:
            self.storage[ident][key] = value
        else:
            self.storage[ident] = {key: value}  # 执行此句的时候又会触发__setattr__方法,所有就进入了死循环

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


local = CustomLocal()


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


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

5. 自定义local对象,基于面向对象-升级版支持协程:

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

from threading import Thread


class CustomLocal(object):
    """
    自定义local对象,基于面向对象
    为每个线程(协程)开辟一块空间进行数据的存储
    空间与空间之间是隔离的
    """
    def __init__(self):
        # self.storage = {}  # 执行此句代码的时候会先触发__setattr__方法
        # 为了避免报错:RecursionError: maximum recursion depth exceeded while calling a Python object
        # 需要先把storage创建出来,所以调用父类的__setattr__方法
        super(CustomLocal, self).__setattr__("storage", {})

    def __setattr__(self, key, value):
        ident = get_ident()
        if ident in self.storage:
            self.storage[ident][key] = value
        else:
            self.storage[ident] = {key: value}  # 执行此句的时候又会触发__setattr__方法,所有就进入了死循环

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


local = CustomLocal()


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


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

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

专职

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值