V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
tcdh
V2EX  ›  Python

关于 CPython 线程安全的问题

  •  
  •   tcdh · 2019-05-31 03:37:24 +08:00 · 2339 次点击
    这是一个创建于 1764 天前的主题,其中的信息可能已经有所发展或是发生改变。

    之前对 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 万不够大吗?

    9 条回复    2019-05-31 10:58:00 +08:00
    aijam
        1
    aijam  
       2019-05-31 03:39:52 +08:00
    GIL 了解下
    tcdh
        2
    tcdh  
    OP
       2019-05-31 03:48:29 +08:00
    @aijam 解释器在运行 bytecode 的时候 GIL 是有可能会丢的,我也加了``sys.setswitchinterval(1)``让这个频率高一些。按理说这种情况下往共享资源里写东西,又不是原子操作,是会有 race condition 的。
    binux
        3
    binux  
       2019-05-31 03:54:00 +08:00 via iPhone
    或许 func 执行比线程创建快多了
    tcdh
        4
    tcdh  
    OP
       2019-05-31 03:58:02 +08:00
    @binux 我也猜过这个可能,在 func 里面加过 sleep,但是还是返回了正确的值。不过只测试了一遍( 100 万个 sleep 实在是太慢了)。看看还有没有其他可能。
    aijam
        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.

    一秒太慢了
    binux
        6
    binux  
       2019-05-31 04:10:03 +08:00 via iPhone
    @tcdh 在 func 里面循环一百万遍啊
    denonw
        7
    denonw  
       2019-05-31 09:33:03 +08:00
    在函数里面做这个循环,不用这么多线程
    suconghou
        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/
    tcdh
        9
    tcdh  
    OP
       2019-05-31 10:58:00 +08:00
    @suconghou 我又试了下,确实在 python2 下是有问题的,比较了下 func 的 bytecode 也不一样。反正 bytecode 这种东西也没有标准,说变就变,那就不钻牛角尖了。感谢。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5384 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 07:34 · PVG 15:34 · LAX 00:34 · JFK 03:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.