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
monetto
V2EX  ›  Python

Python 中 有 CAS 的实现吗

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

    Google 了一下 "Python" "CAS",只是搜到了一些关于 GIL 的 ATOMIC 操作。

    但是觉得 Python 的 Lock() 又有点沉,有大佬知道有没有类似 CAS 的轻量级实现吗?

    第 1 条附言  ·  50 天前
    统一回复了,我是使用一个标记变量 + 短代码实现类似 CAS 的效果,在 for _ in range(1000000):加法中四个线程大概用时 5 秒,并保证了总数是 4000000 。
    而直接使用 Lock 耗时是 17 秒。

    GIL 在 Python2 中是 1000 行字节码切换线程,Python3 中是 15 毫秒切换线程。list.pop 这种 C 调用是 atomic 的。
    51 条回复    2021-10-20 12:46:34 +08:00
    dqzcwxb
        1
    dqzcwxb  
       51 天前
    等一个莫名其妙喷 java 的
    不过也有可能没有,因为 cas 不多学点 java 还真不知道是什么
    Goat121
        2
    Goat121  
       51 天前
    @dqzcwxb java 被喷不是语言的问题,正是因为你这种 java boy 太多,让我等羞于为伍,说自己写过几年 java 都心惊胆战怕被人误认为你们。
    cas 和 java 又有什么关系了?人家实现 cas 的时候还没现代操作系统呢,你家 jvm 都没地方跑谢谢。
    dqzcwxb
        3
    dqzcwxb  
       51 天前
    @Goat121 #2 "等一个莫名其妙喷 java 的"你可真会咬钩
    我没说 cas 跟 java 有关系,只是某些喷 java 的喜欢带上
    同理设计模式也跟 java 没啥关系,还不是天天喷 java 设计模式滥用?
    还羞与为伍,笑死
    BBCCBB
        4
    BBCCBB  
       51 天前
    没发现有, python 单进程的, 直接用 Lock().
    janxin
        5
    janxin  
       51 天前
    @dqzcwxb ???为什么喷 Java 要带上 CAS 啊,你说设计模式我还能理解
    est
        6
    est  
       51 天前
    没有而且不需要。因为 GIL 直接保证了同步。
    monetto
        7
    monetto  
    OP
       51 天前
    @est GIL 不等于线程安全,GIL 不保证线程变量同步。
    dqzcwxb
        8
    dqzcwxb  
       51 天前
    @janxin #5 设计模式不是 java 独有的,也不是 java 发明
    cas 不是 java 独有的,也不是 java 发明
    securityCoding
        9
    securityCoding  
       51 天前
    设计模式跟语言无关 , 基本上有点名气的开源项目里面都是设计模式的影子在里面
    stach
        11
    stach  
       51 天前
    Python 一般我都用 Lock() 来确保类似 i += 1 这种操作的原子性, 之前查找资料有实现原子性的一些库, 不过没用过.
    ipwx
        12
    ipwx  
       51 天前
    Python 的 CAS 将是一场灾难啊。。。GIL 把一个忙等待的线程切了进来。。。
    Trim21
        13
    Trim21  
       51 天前
    @est #6 保证不了,不说 if 和赋值之间会不会切线程,就是赋值也不一定安全
    sujin190
        14
    sujin190  
       51 天前
    CAS 是 cpu 指令级提供的冲突处理的,python 一个+1 操作都好多条 cpu 指令了,这个在 python 层面上来看已经毫无意义了吧
    abersheeran
        15
    abersheeran  
       51 天前   ❤️ 1
    比较难想象 Python 怎么 CAS,l.a = b 不是原子的,这要变成三条字节码,两个 LOAD_NAME 和一个 STORE_NAME 。
    如果直接用 a = b,那你压根拿不到 a 的结果,因为 Python 没有指针,函数里赋值过去就没了。
    如果用 nonlocal global 去修复拿结果的问题,那就没办法做到无副作用,而且这只是换了个 name,对象所在的内存还是没有变化。

    综上所述,建议直接 threading.Lock,不会有人上了 Python 还扣这个锁的性能吧?大可不必啊
    abersheeran
        16
    abersheeran  
       51 天前
    @sujin190 在 CPython 虚拟机里,是按照字节码来逐个执行的。对于一个单进程 CPython 程序来说,字节码层面的原子性就足够了,CPU 级别的才是没有意义。
    est
        17
    est  
       51 天前
    @Trim21 赋值都不安全?看来我孤陋寡闻了。233
    sujin190
        18
    sujin190  
       51 天前   ❤️ 1
    @abersheeran #16 但这个实现没啥现实意义啊,CPU 级 CAS 的价值是在多核中,CAS 可以通过占用少量 CPU 时间来换取通过更重度的锁来导致线程休眠切换调度的问题,同时还可以最大限度保护内核缓存失效的问题,毕竟现代 cpu 和缓存速度太快了,但是你在 Python 层面做一个这东西似乎啥都没解决吧,没啥意义确实没啥提供的必要
    abersheeran
        19
    abersheeran  
       51 天前
    @sujin190 为了 CAS 而上 CAS 的人还是挺多的,毕竟是吹嘘的资本,尽管在他们手里几乎解决不了任何实际问题。
    sujin190
        20
    sujin190  
       51 天前
    @est #17 最简单一条赋值语句一般有三条 bytecode 组成,单条 bytecode 是线程安全的,多条之间不是,你可以用 dis 显示下编译出的 bytecode 就知道了
    sujin190
        21
    sujin190  
       51 天前
    @abersheeran #19 确实了,Python 上 CAS 确实是为了 CAS 而 CAS 了,毕竟 GIL 限制了,不过如果你搞个扩展释放掉 GIL 的话就可以搞了
    sampeng
        22
    sampeng  
       51 天前
    看完讨论。。我以为是 CAS 单点登录,我还纳闷这有啥好讨论的。。。落伍了落伍了
    fgwmlhdkkkw
        23
    fgwmlhdkkkw  
       51 天前
    搜 atomic 好像有几个,试试看?
    woctordho
        24
    woctordho  
       51 天前   ❤️ 1
    我还以为是 computer algebra system,差点开始安利 SageMath (
    abersheeran
        25
    abersheeran  
       51 天前
    @sujin190 搞扩展那也是别的语言了,一般是 C,C 的 CAS 这资料还不到处都是,可以说跟 Python 一点关系都没有了。哈哈哈。
    junkun
        26
    junkun  
       51 天前
    yunluw
        27
    yunluw  
       51 天前   ❤️ 1
    此贴前三楼以自己为示范展示了不加控制的人类本性
    dingwen07
        28
    dingwen07  
       51 天前 via iPhone
    点击来之前以为是计算机代数系统
    guoli100
        29
    guoli100  
       51 天前
    @dingwen07 我第一眼也以为是说计算机代数系统
    2i2Re2PLMaDnghL
        30
    2i2Re2PLMaDnghL  
       51 天前
    @securityCoding 设计模式的影子就不叫设计模式。设计模式的全部想法都是在(拙劣、错误且反思考地)尝试提取习惯性操作并为之取个名字。
    你这就好比大多数语言给 a=1 取个名字叫「赋值」,然后说『( Haskell 的) let a=1 in ... 有赋值的影子』,这说明你被词汇和词义限制住了思想。
    Hstar
        31
    Hstar  
       51 天前
    据我所知,Python 只有.append() 是 aotmic 的,有点小坑但是有 GIL 了
    imycc
        32
    imycc  
       51 天前
    恕我孤陋寡闻,python 确保线程安全的一般手段应该就是 threading.Lock,还真没见过其他方式
    Jooooooooo
        33
    Jooooooooo  
       51 天前
    @dqzcwxb 有毒. 楼主完全没提 java 你 1L 莫名其妙来个喷 Java?
    future0906
        34
    future0906  
       51 天前
    @Trim21 不是,你就好好学学啥是 GIL 。threading.Lock 主要是保证 PythonVM 外的临界区的。
    Trim21
        35
    Trim21  
       51 天前
    你可以直接说你想表达的东西
    Trim21
        36
    Trim21  
       51 天前
    @future0906 #34 你可以直接说你想表达的东西
    msaionyc
        37
    msaionyc  
       50 天前 via iPhone   ❤️ 1
    1 楼这种真的是毒瘤,立个靶子自己打,败坏社区氛围,引战
    dingyaguang117
        38
    dingyaguang117  
       50 天前 via iPhone
    @future0906 真的吗?有了 GIL 什么都不用考虑了?
    monetto
        39
    monetto  
    OP
       50 天前
    @dingyaguang117 Python2 中 GIL 是运行 1000 行字节码,然后切换线程,Python3 是 15 毫秒切换线程。GIL 跟线程安全根本毫无关系,除非你能精准的控制字节码的数量。其次就是,dict.append,list.pop 这种 C 语言调用实现,是 Atomic 的。
    monetto
        40
    monetto  
    OP
       50 天前
    @monetto dict 没有 append,说错了,remove
    sujin190
        41
    sujin190  
       50 天前
    def f():
    ...: a=1
    ...: b=a


    2 0 LOAD_CONST 1 (1)
    2 STORE_FAST 0 (a)

    3 4 LOAD_FAST 0 (a)
    6 STORE_FAST 1 (b)
    8 LOAD_CONST 0 (None)

    @junkun #26 一个赋值语句两条字节码组成,以 Python 的内存结构来说,其实就算被拆开其实越不会有副作用,所以认为是原子操作其实也没有啥问题
    sujin190
        42
    sujin190  
       50 天前
    @monetto #39 Python 的 GIL 就是用来保证内部数据结构线程安全的,否则直接删掉 GIL 就是了啊,所以肯定必须要求是单条字节码完整执行完成才能切换,不可能像操作系统线程一样时间一到啥都不管就直接切换吧,否则 Python 内部自身的数据结构都有可能被破坏了,从这一点上来说,GIL 保证单条字节码的完整性,确实提供了大量线程安全的操作,比如赋值,不能说毫无关系吧
    monetto
        43
    monetto  
    OP
       50 天前
    @sujin190 嗯嗯,毫无关系确实不至于,对象内部的线程安全确实没错,不过问题是只有 CPython 存在 GIL,JPython 是没有的。
    dingyaguang117
        44
    dingyaguang117  
       49 天前 via iPhone
    @sujin190 GIL 是用来保证对象内部线程安全怎么理解,什么是内部对象,那些场景线程安全?
    dingyaguang117
        45
    dingyaguang117  
       49 天前 via iPhone
    @sujin190 应该是只有单字节码的操作才能保证线程安全。a=a+1 应该都不是
    dingyaguang117
        46
    dingyaguang117  
       49 天前 via iPhone
    @monetto GIL 锁不能完全解决线程安全问题,举个例子:一个线程遍历 dict,另一个删除。线程安全问题不仅是一行语句的安全
    monetto
        47
    monetto  
    OP
       49 天前
    @dingyaguang117 晕...我上面不是说了吗...保证对象的 pop,append 线程安全,内部的,不是两个线程同时操作。我没说两个线程同时操作线程安全。因为 pop 和 append 是 C 语言调用,C 语言调用的都是 ATOMIC 的。
    monetto
        48
    monetto  
    OP
       49 天前
    @dingyaguang117 人家说的是内部数据结构,是 pop,remove 等内部的操作,不是让你两个线程同时操作的...理解错了吧兄弟
    sujin190
        49
    sujin190  
       49 天前 via Android
    @dingyaguang117 Python 解释器也需要用内存,也有数据结构啊,编译,创建类,模块导入,线程创建,文件操作,套接字这些底层也是用 GIL 保证线程安全的吧
    sujin190
        50
    sujin190  
       49 天前 via Android
    @dingyaguang117 其实 dict 迭代器生成,删除其实就一天字节码指令,线程安全的逻辑并没有改变,a=a+1 应该是三条指令,加载、计算和保存,事实上除了计算,前两条指令不会有副作用,加法因为内存结构是指针操作,这么看其实这个其实也没有并发安全问题,但是大多数情况下这个操作需要加锁,估计是后面大概率就是判断语句,而这两之间就不是线程安全的了吧
    sujin190
        51
    sujin190  
       49 天前 via Android
    @dingyaguang117 好吧,纠正一下,数字加法指令会返回新的数字对象,所以保存这个字节码在多线程下这种情况就不是线程安全的了
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1171 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 19:31 · PVG 03:31 · LAX 11:31 · JFK 14:31
    ♥ Do have faith in what you're doing.