首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么这个带有线程的Python代码具有竞争条件?

为什么这个带有线程的Python代码具有竞争条件?
EN

Stack Overflow用户
提问于 2021-12-27 08:55:35
回答 2查看 632关注 0票数 7

此代码创建一个竞赛条件:

代码语言:javascript
复制
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]}')

输出:

代码语言:javascript
复制
$ 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版本:

代码语言:javascript
复制
Python 3.9.7 (default, Sep 10 2021, 14:59:43) 
[GCC 11.2.0] on linux

我认为吉尔会阻止它,并且不允许两个线程一起运行,直到它们做一些与io相关的事情或者调用C库。至少这是您从医生们中得出的结论。

那么,GIL实际上是做什么的,线程何时并行运行?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-12-27 09:23:52

更好地阅读医生们,我认为答案是:

CPython解释器用于确保一次只执行一个字节码的机制。这通过使对象模型(包括关键的内置类型(如dict) )隐式地避免并发访问,从而简化了CPython实现。锁定整个解释器使解释器更容易被多线程处理,而牺牲了多处理器机器提供的大部分并行性。然而,一些扩展模块,无论是标准的还是第三方的,都是为了在执行压缩或散列等计算密集型任务时释放GIL。同时,在执行I/O时,GIL总是被释放。

我不知道这个字节码的内部结构,但我猜这个字节码的每一行或每一个块都是单独执行的,其他线程也在等待(这使它慢了下来)。但有些行由多个块组成,并不是原子的。

如果运行dis.dis('x[0] += 1'),如下所示

代码语言:javascript
复制
          0 LOAD_NAME                0 (x)
          2 LOAD_CONST               0 (0)
          4 DUP_TOP_TWO
          6 BINARY_SUBSCR
          8 LOAD_CONST               1 (1)
         10 INPLACE_ADD
         12 ROT_THREE
         14 STORE_SUBSCR
         16 LOAD_CONST               2 (None)
         18 RETURN_VALUE

其中一些是以并发方式执行的,并使竞争条件成为可能。因此,吉尔只保证像listdict这样的结构的内部不会被破坏。

票数 2
EN

Stack Overflow用户

发布于 2022-09-13 06:44:39

根据我们最后的注释,这似乎已经用python version 3.10和更高的版本修复了(ubuntu,windows)。这个问题已经没有经验了。

然而,在其他情况下,种族条件是可以遵守的。例如:

代码语言:javascript
复制
import threading
import time
 
x = 10
 
def increment(by):
    global x
 
    local_counter = x
    local_counter += by
 
    time.sleep(1)
 
    x = local_counter
    print(f'{threading.current_thread().name} inc x {by}, x: {x}')
 
def main():
    # creating threads
    t1 = threading.Thread(target=increment, args=(5,))
    t2 = threading.Thread(target=increment, args=(10,))
   
    # starting the threads
    t1.start()
    t2.start()
   
    # waiting for the threads to complete
    t1.join()
    t2.join()
   
    print(f'The final value of x is {x}')
 
for i in range(10):
    main()

它产生了这样的结果:

代码语言:javascript
复制
Thread-56 (increment) inc x 10, x: 20Thread-55 (increment) inc x 5, x: 15
 
The final value of x is 15
Thread-57 (increment) inc x 5, x: 20Thread-58 (increment) inc x 10, x: 25
 
The final value of x is 25
Thread-60 (increment) inc x 10, x: 35Thread-59 (increment) inc x 5, x: 30
 
The final value of x is 30
Thread-61 (increment) inc x 5, x: 35
Thread-62 (increment) inc x 10, x: 40
The final value of x is 40
Thread-64 (increment) inc x 10, x: 50Thread-63 (increment) inc x 5, x: 45
 
The final value of x is 45

但是这里的解决方法是使用asyncio模块来控制代码流。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70493438

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档