Threadlocal-线程隔离的全局变量

多线程里,多个线程操作同一个全局对象会出现互相干扰的情况,为避免干扰,每个线程会使用局部变量,但是局部变量在单个线程的不同函数里使用需要不停的通过参数传递才能获得,这样很麻烦,python的多线程模块threading里提供了一个全局对象local, 每个线程可以使用这个对象来绑定属性值,且只能看到自己

下面通过三种情况来说明:

  • 普通全局变量的多线程干扰情况
  • 局部变量的冗余参数传递
  • threading.local 全局变量的线程隔离

1.普通全局变量的多线程干扰情况

import threading
import time
 
x = 0 #全局变量,所有子线程都可以修改
def worker():
    global x  # 指明是全局变量
    for i in range(100):
        time.sleep(0.01)
        x += 1
    print(threading.current_thread(),x)
 
for i in range(10):
    threading.Thread(target=worker).start()

输出:
 <Thread(Thread-1, started 11104)>
 <Thread(Thread-4, started 17116)>
 <Thread(Thread-3, started 15488)>
 <Thread(Thread-2, started 17128)>
 <Thread(Thread-5, started 16872)>
 <Thread(Thread-6, started 4600)>
 <Thread(Thread-9, started 13152)>
 <Thread(Thread-7, started 14368)>
 <Thread(Thread-10, started 16768)>
 <Thread(Thread-8, started 13592)>          
 991
 993
 994
 992
 995
 996
 998
 999
 1000
 997

分析输出:如果多线程之间不干扰应该都输出100的,但是因为每个线程都对全局变量x进行修改,造成x的值被10个线程累加,最早结束的线程都累加到了991,这就是普通全局变量在多线程操作里的干扰现象

2.局部变量的冗余参数传递

import threading
import time
 
def worker():
    x=0
    for i in range(100):
        time.sleep(0.01)
        x += 1
    print(threading.current_thread(), x)
 
for i in range(10):
    threading.Thread(target=worker).start()

输出:
Thread(Thread-1, started 596)>
<Thread(Thread-6, started 18256)>
<Thread(Thread-4, started 13020)>
<Thread(Thread-5, started 7836)>
<Thread(Thread-8, started 15452)>
<Thread(Thread-2, started 13852)>
<Thread(Thread-7, started 15708)>
<Thread(Thread-9, started 8248)>
<Thread(Thread-3, started 14560)>
<Thread(Thread-10, started 16908)>          
100
100
100
100
100
100
100
100
100
100

上面这种使用局部变量的方式可以确保每个子线程使用的都是单独的变量x,所以不会互相干扰,看上去很完美,但是如果单线程里有很多函数的话,每次调用都要显示传递x参数,例如:

import threading
import time
 
def worker():
    x=0
    for i in range(100):
        time.sleep(0.01)
        x += 1
    print1(x)
    print2(x)
    print(x)
 
def print1(x):
    print('aaaaaa',x)

def print2(x):
    print('bbbbbb',x)

for i in range(10):
    threading.Thread(target=worker).start()

上面这种要在单个线程里调用其它函数的时候,每次都要把局部变量x作为参数传递过去,这点类似django的视图函数,每个view函数的第一个参数都固定为request来接受请求的数据

3.threading.local 全局变量的线程隔离

import threading
import time
 
x = threading.local()#全局对象
 
def worker():
    x.num = 0
    for i in range(100):
        time.sleep(0.01)
        x.num += 1
    print1()
    print2()
    print(threading.current_thread(),x.num)
 
for i in range(10):
    threading.Thread(target=worker).start()


def print1():
    print('aaaaaa',x.num)

def print2():
    print('bbbbbb',x.num)

输出:(为了显示整洁,这里输出格式经过手动调整)
<Thread(Thread-3, started 14320)>
<Thread(Thread-1, started 16268)>
<Thread(Thread-2, started 16464)>
<Thread(Thread-9, started 16388)>
<Thread(Thread-6, started 15836)>
<Thread(Thread-8, started 12512)>
<Thread(Thread-5, started 15552)>
<Thread(Thread-10, started 1568)>
<Thread(Thread-4, started 9252)> 
<Thread(Thread-7, started 18128)> 
aaaaa 100
bbbbb 100
100
aaaaa 100
bbbbb 100
100
aaaaa 100
bbbbb 100
100
aaaaa 100
bbbbb 100
100
aaaaa 100
bbbbb 100
100
aaaaa 100
bbbbb 100
100
aaaaa 100
bbbbb 100
100
aaaaa 100
bbbbb 100
100
aaaaa 100
bbbbb 100
100
aaaaa 100
bbbbb 100
100

分析:
主线程里定义了threading.local全局对象x, 这个x有点类型字典,在每个子线程里都为这个x绑定了一个num属性,并且在循环里累加这个num属性,可以看到,这10个子线程对x的操作没有互相干扰,因为全局对象x为每个线程维护了独立字典,这里就相当于维护了10个独立字典,而这些字典都绑定了一个线程id, 所以每个线程来获取数据时,会通过线程的id来获取相应的字典,这就做到了每个线程只能获取到自己的数据。

同时,在调用print1(),print1()函数时,并没有像普通局部变量那样显示传递参数。
这其实就是flask里全局request对象的原理,flask的视图函数不需要像django那样需要request固定作为第一个参数,而是使用全局的request对象,import request 后就可以在任何视图函数里使用这个request对象了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348