线程局部变量--threading.local

一、threading.local的功能了解

通过下面两个例子可以直观的了解到local的作用。

1.普通线程操作同一变量:

import time
import threading

class Foo(object):
    def __init__(self):
        self.num = 0

val2 = Foo()

def task(i):
    val2.num = i
    time.sleep(1)
    print(val2.num)

for i in range(4):
    t = threading.Thread(target=task,args=(i,))
    t.start()
    
"""
3
3
3
3

"""

​2.local操作同一变量:

import time
import threading

# 当每个线程在执行 val1.xx=1 ,在内部会为此线程开辟一个空间,来存储 xx=1
# val1.xx,找到此线程自己的内存地址去取自己存储 xx
val1 = threading.local()

def task(i):
    val1.num = i
    time.sleep(1)
    print(val1.num)

for i in range(4):
    t = threading.Thread(target=task,args=(i,))
    t.start()
    
"""
0
1
2
3

"""

local可以返回一个线程局部变量,通过使用线程局部变量可以很简捷地隔离多线程访问的竞争资源,从而简化多线程井发访问的编程处理。

​ 线程局部变量(Thread Local Variable)的功用其实非常简单,就是为每一个使用该变量的线程都提供一个变量的副本,使每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量一样。

线程局部变量与同步机制的区别:
- 线程局部变量和其他同步机制一样,都是为了解决多线程中对共享资源的访问冲突的。在普通的同步机制中,是通过为对象加锁来实现多个线程对共享资源的安全访问的。由于共享资源是多个线程共享的,所以要使用这种同步机制,就需要很细致地分析什么时候对共享资源进行读写,什么时候需要锁定该资源,什么时候释放对该资源的锁定等。在这种情况下,系统并没有将这份资源复制多份,只是采用安全机制来控制对这份资源的的访问而已。

​ - 线程局部变量从另一个角度来解决多线程的并发访问问题。线程局部变量将需要并发访问的资源复制多份,每个线程都拥有自己的资源副本,从而也就没有必要对该资源进行同步了。线程局部变量提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量放到线程局部变量中,或者把该对象中与线程相关的状态放入线程局部变量中保存。

​ - 线程局部变量并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对共享资源的并发访问,是多个线程之间进行通信的有效方式;而线程局部变量是为了隔离多个线程的数据共享,从根本上避免多个钱程之间对共享资源(变量)的竞争,也就不需要对多个线程进行同步了。

通常建议,如果多个线程之间需要共享资源,以实现线程通信,则使用同步机制;如果仅仅需要隔离多个线程之间的共享冲突,则可以使用线程局部变量。

二、线程的唯一标识

import threading
from threading import get_ident

def task():
    ident = get_ident()
    print(ident)
for i in range(20):
    t = threading.Thread(target=task)
    t.start()

三、自定义threading.local

import threading
"""
storage = {
    1111:{'x1':[0,1,2,3]},
    1112:{'x1':1}
    1113:{'x1':2}
    1114:{'x1':3}
    1115:{'x1':4}
}
"""
class Local(object):
    def __init__(self):
        object.__setattr__(self,'storage',{})

    def __setattr__(self, key, value):
        ident = threading.get_ident()
        if ident in self.storage:
            self.storage[ident][key] = value
        else:
            self.storage[ident] = {key:value}

    def __getattr__(self, item):
        ident = threading.get_ident()
        if ident not in self.storage:
            return
        return self.storage[ident].get(item)

local = Local()

def task(arg):
    local.x1 = arg
    print(local.x1)

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

四、加强版threading.local

import threading
"""
storage = {
    1111:{'x1':[]},
    1112:{'x1':[]}
    1113:{'x1':[]}
    1114:{'x1':[]}
    1115:{'x1':[]},
    1116:{'x1':[]}
}
"""
class Local(object):
    def __init__(self):
        object.__setattr__(self,'storage',{})

    def __setattr__(self, key, value):
        ident = threading.get_ident()
        if ident in self.storage:
            self.storage[ident][key].append(value)
        else:
            self.storage[ident] = {key:[value,]}

    def __getattr__(self, item):
        ident = threading.get_ident()
        if ident not in self.storage:
            return
        return self.storage[ident][item][-1]

local = Local()

def task(arg):
    local.x1 = arg
    print(local.x1)

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

flask源码关于local的实现(Local具体分析见下一章)

try:
    # 协程
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident
"""
__storage__ = {
    1111:{"stack":[xx] }
}
"""
class Local(object):

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

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

    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__() # 1111
        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)
posted @ 2019-11-23 13:31  SensorError  阅读(326)  评论(0编辑  收藏  举报