首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python 学习手册
Python Cookbook
Python 基础教程
Python Sites
PyPI - Python Package Index
http://www.simple-is-better.com/
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
V2EX  ›  Python

关于 CPython 线程安全的问题

  •  
  •   tcdh · 165 天前 · 1211 次点击
    这是一个创建于 165 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    一秒太慢了
        6
    binux   165 天前 via iPhone
    @tcdh 在 func 里面循环一百万遍啊
        7
    denonw   165 天前
    在函数里面做这个循环,不用这么多线程
        8
    suconghou   165 天前
    ```
    # 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/
        9
    tcdh   165 天前
    @suconghou 我又试了下,确实在 python2 下是有问题的,比较了下 func 的 bytecode 也不一样。反正 bytecode 这种东西也没有标准,说变就变,那就不钻牛角尖了。感谢。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2678 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 38ms · UTC 12:54 · PVG 20:54 · LAX 04:54 · JFK 07:54
    ♥ Do have faith in what you're doing.