0%

threading.local笔记

普通多线程修改同一数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import threading
import time

v = 0

def task(n):
global v
v = n
time.sleep(0.1)
print(v)

l = []
for i in range(10):
t = threading.Thread(target=task, args=(i,))
l.append(t)
t.start()

for p in l:
p.join()


'''
结果为
9
9
9
9
9
9 ...
'''

当多线程同时去修改同一个变量时,会出现数据不安全的情况,一般情况下会使用互斥锁来解决,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import threading
import time

lock = threading.Lock()
v = 0

def task(n):
global v
lock.acquire()
v = n
time.sleep(0.1)
lock.release()

print(v)

l = []
for i in range(10):
t = threading.Thread(target=task, args=(i,))
l.append(t)
t.start()

for p in l:
p.join()

'''
结果为
0
1
2
3
4 ...
'''

threading.local对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import threading
import time

obj = threading.local()


def task(n):
obj.a = n
time.sleep(0.1)

print(obj.a)

l = []
for i in range(10):
t = threading.Thread(target=task, args=(i,))
l.append(t)
t.start()

for p in l:
p.join()

'''
结果为
5
2
6
1
0 ...
'''

threading.local作用:为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离)。

问题:

  • 如何获取一个线程的唯一标记? threading.get_ident()

  • 根据字典自定义一个类似于threading.local功能?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import threading
    import time

    DIC = {}

    def task(i):
    ident = threading.get_ident()
    if ident in DIC:
    DIC[ident]["stack"] = i
    else:
    DIC[ident] = {"stack": i}

    time.sleep(1)

    print(DIC[ident]["stack"])

    l = []
    for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    l.append(t)
    t.start()

    for p in l:
    p.join()
  • 根据字典自定义一个为每个协程开辟空间进行存取数据。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import threading
    import greenlet
    import time

    DIC = {}

    def task(i):
    # ident = threading.get_ident()
    ident = greenlet.getcurrent()
    if ident in DIC:
    DIC[ident]["stack"] = i
    else:
    DIC[ident] = {"stack": i}

    time.sleep(1)

    print(DIC[ident]["stack"])

    l = []
    for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    l.append(t)
    t.start()

    for p in l:
    p.join()
  • 通过getattr/setattr 构造出来 threading.local的加强版(协程)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    import threading
    import time

    # 如果没有协程就使用线程ID
    try:
    import greenlet
    get_ident = greenlet.getcurrent
    except ImportError:
    get_ident = threading.get_ident

    class Local(object):
    DIC = {}

    # 获取
    def __getattr__(self, item):
    ident = get_ident()
    if ident in self.DIC:
    return self.DIC[ident].get(item)
    return None

    # 设置
    def __setattr__(self, key, value):
    ident = get_ident()
    if ident in self.DIC:
    self.DIC[ident][key] = value
    else:
    self.DIC[ident] = {key: value}

    obj = Local()

    def task(i):
    obj.stack = i
    time.sleep(1)
    print(obj.stack)

    l = []
    for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    l.append(t)
    t.start()

    for p in l:
    p.join()