之前对 Python 理解不深,最近准备深入学习一下,在多线程和线程安全的时候碰到了一个问题。
https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe 里写到 Python 自带的数据结构的某些操作是不安全的。比如 D[x] = D[x] + 1
。
于是写了个很简单的测试:
import threading
import sys
sys.setswitchinterval(1)
d = {1:0}
def func():
d[1] += 1
threads = []
for _ in range(1000000):
t = threading.Thread(target=func)
threads.append(t)
t.start()
for thread in threads:
thread.join()
print(d)
但是跑了很多遍结果都没有问题(打印{1: 100000})。用 dis 看了一下,确实 func()也是用了多行 bytecode,按理说应该有 race condition 才对。
>>> dis.dis(func)
11 0 LOAD_GLOBAL 0 (d)
2 LOAD_CONST 1 (1)
4 DUP_TOP_TWO
6 BINARY_SUBSCR
8 LOAD_CONST 1 (1)
10 INPLACE_ADD
12 ROT_THREE
14 STORE_SUBSCR
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
不太明白问题出在哪,是 100 万不够大吗?
1
aijam 2019-05-31 03:39:52 +08:00
GIL 了解下
|
2
tcdh OP @aijam 解释器在运行 bytecode 的时候 GIL 是有可能会丢的,我也加了``sys.setswitchinterval(1)``让这个频率高一些。按理说这种情况下往共享资源里写东西,又不是原子操作,是会有 race condition 的。
) |
3
binux 2019-05-31 03:54:00 +08:00 via iPhone
或许 func 执行比线程创建快多了
|
4
tcdh OP @binux 我也猜过这个可能,在 func 里面加过 sleep,但是还是返回了正确的值。不过只测试了一遍( 100 万个 sleep 实在是太慢了)。看看还有没有其他可能。
|
5
aijam 2019-05-31 04:01:12 +08:00
@tcdh sys.setswitchinterval(interval)
Set the interpreter ’ s thread switch interval (in seconds). This floating-point value determines the ideal duration of the “ timeslices ” allocated to concurrently running Python threads. 一秒太慢了 |
7
denonw 2019-05-31 09:33:03 +08:00
在函数里面做这个循环,不用这么多线程
|
8
suconghou 2019-05-31 10:39:20 +08:00
```
# encoding: utf-8 import threading import time d = {1:0} def func(): for _ in range(10): d[1] += 1 time.sleep(0.01) d[1] += 1 threads = [] for _ in range(1000): t = threading.Thread(target=func) threads.append(t) t.start() for thread in threads: thread.join() print(d) ``` python2 下有问题 python3 上没问题 https://www.reddit.com/r/Python/comments/4vyg2m/threadinglocking_is_4x_as_fast_on_python_3_vs/d62giao/ |