【发布时间】:2021-12-27 08:55:35
【问题描述】:
这段代码创建了一个竞争条件:
import threading
ITERS = 100000
x = [0]
def worker():
for _ in range(ITERS):
x[0] += 1 # this line creates a race condition
# because it takes a value, increments and then writes
# some inrcements can be done together, and lost
def main():
x[0] = 0 # you may use `global x` instead of this list trick too
t1 = threading.Thread(target=worker)
t2 = threading.Thread(target=worker)
t1.start()
t2.start()
t1.join()
t2.join()
for i in range(5):
main()
print(f'iteration {i}. expected x = {ITERS*2}, got {x[0]}')
输出:
$ python3 test.py
iteration 0. expected x = 200000, got 200000
iteration 1. expected x = 200000, got 148115
iteration 2. expected x = 200000, got 155071
iteration 3. expected x = 200000, got 200000
iteration 4. expected x = 200000, got 200000
Python3 版本:
Python 3.9.7 (default, Sep 10 2021, 14:59:43)
[GCC 11.2.0] on linux
我认为 GIL 会阻止它并且不允许两个线程一起运行,直到它们执行与 io 相关的操作或调用 C 库。至少这是你可以从the docs 得出的结论。
那么,GIL 究竟做了什么,线程什么时候并行运行?
【问题讨论】:
-
这不应该是锁的工作吗?我的意思是为什么在添加列表编号时不使用锁?对列表的操作不保证原子性。您是否尝试过使用锁并发生同样的情况?另外我认为使用双端队列应该比简单的列表确保更多的原子性。有一个 * 答案,Alex martelli 指出,在使用线程时你应该结合使用双端队列和队列
-
@FedericoBaù 当然,当您知道这将发生时。当我十年前学习 Python 并尝试多线程时,它似乎应该像 Javascript 一样,执行整个函数直到它结束并让事件循环继续。
-
用 Python 3.10 试试吧,我认为最近有一个有趣的问答指出这种情况不会再发生(尽管假设它不会发生并不安全)。
标签: python python-3.x gil