深入理解Python的TLS机制和Threading.local()

  • 时间:
  • 浏览:4
  • 来源:5分3D_5分排列5

from threading import current_thread

class A:

1.背景介绍

我过后写过另另1个关于Python的TLS机制的浅浅析,你们都都歌词 歌词 可不都都还可以参考你這個 文章,首先,你们都都歌词 歌词 再来熟悉熟悉有哪些是TLS机制。

Session1.join()

Session2.join()

上述Threadlocal的实现原理类式有另另1个全局的词典,词典的key是应用应用程序id,value也不我共享的全局变量的副本。每次访问全局变量的过后,你访问到的确实是副本,也不我Python使用黑魔法帮你们都都歌词 歌词 屏蔽了你這個 userName.val 的访问细节,确实他访问的是词典中的对应应用应用程序所拥有的对象副本。

外理多应用应用程序编程中的对同一变量的访问冲突的有本身技术,TLS会为每另另1个应用应用程序维护另另1个和该应用应用程序绑定的变量的副本。而是否 无止尽的传递局部参数的法子编程。

每另另1个应用应用程序都拥有本人的变量副本,我不多 原因分析分析分析着就一定我不多 对TLS变量中你這個 操作枷锁了。

Java平台的java.lang.ThreadLocal和Python 中的threading.local()是否 TLS技术的有本身实现,。

TLS使用的缺陷是,也不我你的应用应用程序是否 退出,没人 副本数据也不我突然 不被GC回收,会消耗不多资源,比如应用应用程序池中,应用应用程序是否 退出,使用TLS需用非常小心。

TLS技术的实现原理:

需用每个应用应用程序都维护另另1个 key-value集合数据特征,记录每个应用应用程序访问到的 TLS变量副本,原先每个应用应用程序可不都都还可以根据 key来找到相应的 TLS副本数据,对副本数据进行真实的操作,而是否 TLS全局变量也不我静态类(Java中).

2.2 实现源码分析

all = ["local"]

class _localbase(object):

1.1 Thread Local Storage(应用应用程序局部存储)

你這個 概念最早是相对于全局变量来说的,也不我你们都都歌词 歌词 在编程的过后,会涉及到希望所有应用应用程序都才能共享访问同另另1个变量,在 Python/Go/C 中,你们都都歌词 歌词 就可不都都还可以定义另另1个全局变量,原先Global Variable 对多个应用应用程序也不我可见的,也不我同另另1个应用应用程序所有应用应用程序共享地址空间,你们都都歌词 歌词 都可不都都还可以操作。类式,另另1个全局的配置变量或单实例对象,所有应用应用程序就可不都都还可以很方便访问了,也不我仅仅原先有另另1个前提,也不我你這個 变量的并发操作需用是幂等的,读写不影响你们都都歌词 歌词 应用应用程序的正确性。也不我往往多应用应用程序一块儿操作另另1个全局变量,就会影响应用应用程序的正确性,也不我你们都都歌词 歌词 需用枷锁,比如经典的并发加操作。

t1 = threading.Thread(target=t, args = (777,))

t2 = threading.Thread(target=t, args = (888,))

print current_thread().__dict__

t1.start()

t2.start()

t1.join()

t2.join()

print(data.__dict__)

关键技术就在patch底下,Python 底下有不多你這個 patch的替换手段,也不我直接把基础库的你這個 功能和函数直接替加在了第三方库的比如monkey patch. 再次设置属性的过后,设置的 dict 真是否 ThreadLocal本人的,是也不我当前所在应用应用程序的__dict__ 的某另另1个key-value 副本数据,key 也不我应用应用程序访问的某个TLS变量生成的(另另1个应用应用程序可不都都还可以有不多TLS变量,每个有不同的key),value是另另1个dict. object的 setattr默认行为确实也不我在本人的__dict__对象中加在一对key-pair,也不我现在他的__dict__也不我更加在所在应用应用程序的另另1个数据副本词典dict了,黑魔法替换就在这里.

不多ThreadLocal变量使用强调的侧重点没哟这里,更多的是在编程范式底下。确实也不我你這個 过后,你们都都歌词 歌词 某个变量类型不多函数也不我类都需用用,也不我我又我想要写死在代码里,每次传递参数是否 传递你這個 类也不我变量,也不我一旦你這個 类趋于稳定类型上的变化,也不我对于静态类型的语言,不多地方就得修改参数,也不我你這個 变量突然 在应用应用程序代码的参数传递中层层再次出现,你也不我写过代码就会有感觉,也不我你设计的函数API好像一层层的得把另另1个参数传递进去,即使你這個 层好像用没人 你這個 参数。

2.TLS 在Python中的运用和实现

2.1 简单使用

ThreadLocal不仅仅可不都都还可以外理全局变量访问冲突,确实还有你這個 好处,在PEP266含有提到,ThreadLocal变量是可不都都还可以减少指令加速运算的,也不我全局变量往往需用更多的指令(需用for loop)来做查询访问,而ThreadLocal 过后,有了索引表,直接可不都都还可以根小指令找到你這個 对象。

ThreadLocal 实现过程

3 TLS 在Java 中的运用和实现

3.1 简单使用

public class ThreadLocalExample {

userName = threading.local()

A second use case would be multiple threads accumulating information into a global variable. To avoid a race condition, every access to this global variable would have to be protected by a mutex. Alternatively, each thread might accumulate into a thread-local variable (that, by definition, cannot be read from or written to from other threads, implying that there can be no race conditions). Threads then only have to synchronise a final accumulation from their own thread-local variable into a single, truly global variable.

def func2(xxx,yyy,zzz, passwd, db, host="localhost", port=31506, user="root", charset='utf8'):

import threading

Session1.start()

Session2.start()

{'y': 3}

{'x': 1}

{'x': 1, 'y': 777}

{'x': 1, 'y': 777}

也不我A有本身也不我含有你這個 数据,那就没人 简单的直接克隆了,还需用初始化过后的数据填充新的词典,这也是在源码中看过的。

Session1 = threading.Thread(target=SessionThread("User1"))

Session2 = threading.Thread(target=SessionThread("User2"))

需用每个应用应用程序都维护另另1个 key-value 集合数据特征,记录每个应用应用程序访问到的 TLS 变量副本,原先每个应用应用程序可不都都还可以根据 key 来找到相应的 TLS副本数据,对副本数据进行真实的操作,而是否 TLS全局变量也不我静态类(Java中)。在Python中直接很简单的使用了动态数据绑定的词典数据特征,在Java中稍显麻烦,需用实现另另1个类式Map的特征,ThreadLocal.get() 法子确实本质上也是和Python中一样,先获取当前应用应用程序本人的ThreadLocalMap对象(也不我每个应用应用程序维护的TLS key-value集合啦)。再从ThreadLocalMap对象中找出当前的ThreadLocal变量副本,和HashMap一样的采用了链地址法的hash特征。可不都都还可以参考文章Java 多应用应用程序(7): ThreadLocal 的应用及原理。Java 里一般是采用泛型规定你共享的变量类型,也不我每个应用应用程序维护该变量的副本。

下面的例子展示了Python黑魔法的另另1个替换词典的法子,可不都都还可以运行看看

def func1(zzz, passwd, db, host="localhost", port=31506, user="root", charset='utf8'):

import threading

count = 0

lock = threading.RLock()

def inc():

a = A()

a.y = 3

old_dict = a.__dict__

print(old_dict)

d = {'x':1}

a.substitute(d)

print(a.__dict__)

a.y = 777

print(a.__dict__)

print(d)

比如你们都都歌词 歌词 写了另另1个共享的Manager类,你這個 类也不我是用来做数据库连接,网络连接也不我你這個 的做底层管理功能。你们都都歌词 歌词 有不多应用应用程序需用使用你這個 Manager的你這個 功能,也不我你這個 类是否 用来表示有本身情况报告,供所有应用应用程序并发修改其情况报告并将最终修改的结果表现在该类底下(底下count的例子)。Manager也不我可不都都还可以提供给应用应用程序使用你這個 功能,也不我每个应用应用程序可不都都还可以把你這個 Manager克隆一份成为本人的局部变量,本人可不都都还可以随意修改,也不我我不多 影响到你這個 应用应用程序,也不我是克隆的一份。也不我也不我你需用让管理器记录所有的连接操作次数,没人 多应用应用程序对立面的你這個 变量访问比如Count就需用枷锁了。

def getMysqlConn(passwd, db, host="localhost", port=31506, user="root", charset='utf8'):

def _patch(self):

data = local()

print (data.__dict__)

def t(x):

没人 你们都都歌词 歌词 就设计了有本身方案,也不我有原先有本身变量,他是全局的,也不我每个应用应用程序在访问的过后是否 存储一份成为本人的局部变量,修改就我不多 相互影响了。比如 Linux/Unix的 C 应用应用程序库 libc的全局变量errno, 你這個 确实也不我TLS的例子。当系统调用从内核空间返回用户空间时,也不我系统调用出错,没人 便设置errno的值为另另1个负值,原先就不需用每次在函数内内外部定义局部变量。也不我当多应用应用程序的概念和技术被提出后,这套机制就不再适用了,可不都都还可以使用局部变量,也不我不太也不我去更改已有的代码了,比较好的外理方案是让每个应用应用程序是否 本人的errno。实际上,现在的C库函数是否 把出错代码写入全局量errno,也不我通过另另1个函数__errno_location()获取另另1个地址,再把出错代码写入该地址,其意图也不我让不同的应用应用程序使用不同的出错代码存储地点,而errno,现在一般也不我变成了另另1个宏定义。每另另1个应用应用程序是否 维护本人的一份,修改不影响你這個 应用应用程序。

TLS变量本人会根据当前调用他的Thread对象,根据Thread对象得到该应用应用程序维护的 TLS 副本集合,也不我进一步根据当前TLS的key,查到到key对另另1个的TLS副本数据。原先就给每个应用应用程序造成有本身假象,以为你们都都歌词 歌词 可不都都还可以一块儿更新另另1个全局共享变量也不我静态类对象。

from threading import current_thread, RLock

底下的代码你也不我会疯掉。没人 你也不我就考虑想把你這個 参数提出来,当成全局变量算了,哪一层用到了直接用就好了,没人 让我突然 的不停的被当成局部变量传参。文章Alternatives to global variables and passing the same value over a long chain of calls描述了你這個 大问题,也不我你這個 过后再次出现的大问题也不我,也不我你這個 代码应用应用程序会不可控的更改你這個 变量,原因分析分析分析你的应用应用程序趋于稳定未知错误。你把你這個 参数变成全局的暴露出来,没人 基于的假设也不我该参数我不多 被随意修改!一旦你這個 假设崩塌,你的应用应用程序也不我会趋于稳定灾难后果。这不符合软件设计的开闭原则。不多你们都都歌词 歌词 使用TLS技术化解你這個 矛盾。

底下那个例子不多博客用来做ThreadLocal变量的讲解,实际上我确实是有误导的,不恰当的。也不我你這個 共享变量,你需用枷锁,也不我他的目的也不我为了你们都都歌词 歌词 一块儿去更新另另1个共享变量,多应用应用程序环境下需用枷锁。就算你使用ThreadLocal替换也没用,ThreadLocal能替换你這個 Count变量让所有应用应用程序单独存储一份么,不满足需求。你单独存一份,更改过后还得把结果再次写回到全局变量去更新,那写会的过程还是得枷锁。除非使用Golang中的单Channel更新机制,才能外理枷锁。

a = A(shared_x=111, shared_y=222)

a.y = 3

old_dict = a.__dict__

print(old_dict)

d = {'x':1}

a.substitute(d)

print(a.__dict__)

a.y = 777

print(a.__dict__)

print(d)

print(old_dict)

下图也不我访问每个应用应用程序访问过程,实际上操作的是应用应用程序本人的私有数据副本。同需用用注意的还是那句话,使用 ThreadLocal对象不原因分析分析分析着你的应用应用程序不需用再枷锁,比如你這個 ThreadLocal 对象也不我又引用了你這個 共享情况报告的对象,没人 就要对你這個 共享情况报告对象的操作进行枷锁实现同步和互斥。

class local(_localbase):

这是否 原因分析分析分析着ThreadLocal对象我不多 枷锁了? 确实你這個 ThreadLocal和同步没人 关系,他仅仅是提供了有本身方便每个应用应用程序快速访问变量的法子,也不我也不我你這個 对象有本身你這個 共享情况报告需用你们都都歌词 歌词 一块儿维护(比如Count++),你就需用枷锁,尽管每个应用应用程序操作的是ThreadLocal副本。维基百科上有以下原话:

class A:

}

3.2 源码实现

有了Python版本的分析,Java版本就不再多做解释,感兴趣的可不都都还可以看看源码,实现原理肯定是否 大同小异,也不我语言上的差异,原因分析分析分析 Java 不也不我像Python你這個 动态类型语言一样灵活。

def SessionThread(userName_in):