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

关于 Python 的线程锁, Python 线程 lock.release() 优先被当前线程 acquire 么?

  •  
  •   moxiaowei · 2017-06-30 16:18:47 +08:00 · 2626 次点击
    这是一个创建于 2463 天前的主题,其中的信息可能已经有所发展或是发生改变。

    import threading

    import time

    balance = 0

    lock = threading.Lock()

    def run_thread(n): global balance

    for i in range(100):
    
        lock.acquire()
    
        balance = balance + 1
    
        n = n + balance
    
        time.sleep(1)
    
        print(threading.current_thread().name)
    
        print(n)
    
        print("\r\n\r\n")
    
        lock.release()
    

    t1 = threading.Thread(target=run_thread, args=(5, ), name="t1")

    t2 = threading.Thread(target=run_thread, args=(8, ), name="t2")

    t1.start()

    t2.start()

    t1.join()

    t2.join()

    print(balance)

    执行结果: t1 4661

    t1 4758

    t1 4856

    t1 4955

    t1 5055 -------------------这儿是 t1 线程已经执行完毕了

    t2 109---------------------t2 线程开始执行了

    t2 211

    t2 314

    t1 线程全部走完才会走 t2 线程

    26 条回复    2017-07-01 15:10:55 +08:00
    954880786
        1
    954880786  
       2017-06-30 16:23:42 +08:00 via iPhone
    c 是这样的
    moxiaowei
        2
    moxiaowei  
    OP
       2017-06-30 16:24:48 +08:00
    @954880786 关键这是 python
    wwqgtxx
        3
    wwqgtxx  
       2017-06-30 16:33:19 +08:00 via iPhone   ❤️ 1
    @moxiaowei cpython 底层在 linux 上是用 pthread 库实现的
    moxiaowei
        4
    moxiaowei  
    OP
       2017-06-30 16:35:25 +08:00
    @wwqgtxx 那这样到底算不算合理?
    dbow
        5
    dbow  
       2017-06-30 16:40:27 +08:00
    CPython 有 GIL, 代码是单线程执行的, run_thread 一个线程执行完, 另一个线程才能执行, 跟锁没关系。
    wwqgtxx
        6
    wwqgtxx  
       2017-06-30 16:43:26 +08:00 via iPhone
    @dbow gil 只是保证同时只会执行一条语句。并不是需要一个线程彻底执行完才会切换到别的线程
    wwqgtxx
        7
    wwqgtxx  
       2017-06-30 16:44:25 +08:00 via iPhone
    @moxiaowei 合不合理不重要,重要在于他就是这么做了
    hjc4869
        8
    hjc4869  
       2017-06-30 16:57:08 +08:00
    本地执行楼主的代码是 t1 t2 交替的。
    moxiaowei
        9
    moxiaowei  
    OP
       2017-06-30 17:01:20 +08:00
    @hjc4869 没加 lock 是交替的,而且里面有错误的数据,但是我本地加上 lock 之后就不是交替执行了
    dbow
        10
    dbow  
       2017-06-30 17:07:35 +08:00
    改成这样, 应该就是顺序输出 了. 按你的写法哪个先 acuire lock 不一定。
    def run_thread(n):
    with lock:
    code
    moxiaowei
        11
    moxiaowei  
    OP
       2017-06-30 17:08:34 +08:00
    @dbow 好的 我来试试
    moxiaowei
        12
    moxiaowei  
    OP
       2017-06-30 17:09:46 +08:00
    @dbow 运行结果跟我的写法是一致的
    dbow
        13
    dbow  
       2017-06-30 17:14:22 +08:00
    这样就应该写对了, 多线程抢占的程序的结果比较随机, lock 并没有线程优先取得的问题。
    另外 bytecode 的执行 应该是 100 个为单位切换执行
    The interpreter releases the GIL every 100 "ticks".
    661 /* for manipulating the thread switch and periodic "stuff" - used to be
    662 per thread, now just a pair o' globals */
    663 int _Py_CheckInterval = 100;
    moxiaowei
        14
    moxiaowei  
    OP
       2017-06-30 17:19:20 +08:00
    Python 的线程虽然是真正的线程,但解释器执行代码时,有一个 GIL 锁:Global Interpreter Lock,任何 Python 线程执行前,必须先获得 GIL 锁,然后,每执行 100 条字节码,解释器就自动释放 GIL 锁,让别的线程有机会执行。这个 GIL 全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在 Python 中只能交替执行



    这个应该就是所谓的正解
    denonw
        15
    denonw  
       2017-06-30 17:28:23 +08:00
    虽然争夺锁应该是比较随机的,但是在 python 里面由于有 GIL,就和 @dbow 说的一样,会先执行 100 个单位才开始切换。所以原线程有很大可能又重新获得这个锁吧。
    dbow
        16
    dbow  
       2017-06-30 17:28:40 +08:00
    要是刚学 python, 会 C 的话 , 建议直接文档对着源代码看, 免得瞎猜, 浪费时间。https://github.com/python/cpython/blob/master/Python/ceval.c#L1103
    moxiaowei
        17
    moxiaowei  
    OP
       2017-06-30 17:29:47 +08:00
    @dbow 谢谢 我是个 phper,对服务器比较感兴趣就学学 python,以后搞服务器方便
    atempcode
        18
    atempcode  
       2017-06-30 19:56:28 +08:00
    多线程编程的第一原则: 不要对行程执行的先后顺序有任何的 assumption。
    davinci
        19
    davinci  
       2017-06-30 21:52:10 +08:00
    @dbow 假设在执行 90 条字节码时,线程阻塞了,此时解释器会自动释放 GIL 锁吗?
    dbow
        20
    dbow  
       2017-06-30 22:48:36 +08:00
    释放不了, 需要主动写代码释放, 典型的模式是在阻塞之前释放线程锁, 阻塞操作结束后重新取得。
    以 time.sleep()里的这段代码为例,
    Py_BEGIN_ALLOW_THREADS
    rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE);
    Py_END_ALLOW_THREADS
    dbow
        21
    dbow  
       2017-06-30 22:48:48 +08:00
    @davinci 释放不了, 需要主动写代码释放, 典型的模式是在阻塞之前释放线程锁, 阻塞操作结束后重新取得。
    以 time.sleep()里的这段代码为例,
    Py_BEGIN_ALLOW_THREADS
    rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE);
    Py_END_ALLOW_THREADS
    wwqgtxx
        22
    wwqgtxx  
       2017-06-30 23:36:23 +08:00
    @dbow 但是在 Python 层面上看,所有会导致阻塞的系统库操作都会让 Python 解析器释放 GIL 锁
    dbow
        23
    dbow  
       2017-06-30 23:54:28 +08:00
    @wwqgtxx 是的, 主动释放的, 方法就是下面的, 在底层 C 代码释放的。
    Py_BEGIN_ALLOW_THREADS
    rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE);
    Py_END_ALLOW_THREADS
    wwqgtxx
        24
    wwqgtxx  
       2017-07-01 00:18:10 +08:00
    @dbow 我现在就在想在直接用 ctypes 调用 c 类库的时候有没有什么办法可释放掉 GIL,因为有些时候并不想为了调用一个简单的 C 类库而去用 CPYTHON C API 写个封装,而 cytpes 类库默认应该是没有释放 GIL 的,所以会导致在一些耗时的操作上限制了多线程能力的发挥
    dbow
        25
    dbow  
       2017-07-01 10:46:35 +08:00
    @wwqgtxx ctypes 调用 c 函数是自动释放 GIL 的, 除非是调用 python 自己的 C API.

    821 #ifdef WITH_THREAD
    822 if ((flags & FUNCFLAG_PYTHONAPI) == 0)
    823 Py_UNBLOCK_THREADS
    824 #endif
    825 if (flags & FUNCFLAG_USE_ERRNO) {
    826 int temp = space[0];
    827 space[0] = errno;
    828 errno = temp;
    829 }
    830 #ifdef MS_WIN32
    831 if (flags & FUNCFLAG_USE_LASTERROR) {
    832 int temp = space[1];
    833 space[1] = GetLastError();
    834 SetLastError(temp);
    835 }
    wwqgtxx
        26
    wwqgtxx  
       2017-07-01 15:10:55 +08:00 via iPhone
    @dbow 这一段我还真的没有仔细看过,可能是很久前某一篇错误的资料上说他不会自己释放吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5598 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 06:34 · PVG 14:34 · LAX 23:34 · JFK 02:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.