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

2017-06-30 16:18:47 +08:00
 moxiaowei

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 线程

2800 次点击
所在节点    Python
26 条回复
954880786
2017-06-30 16:23:42 +08:00
c 是这样的
moxiaowei
2017-06-30 16:24:48 +08:00
@954880786 关键这是 python
wwqgtxx
2017-06-30 16:33:19 +08:00
@moxiaowei cpython 底层在 linux 上是用 pthread 库实现的
moxiaowei
2017-06-30 16:35:25 +08:00
@wwqgtxx 那这样到底算不算合理?
dbow
2017-06-30 16:40:27 +08:00
CPython 有 GIL, 代码是单线程执行的, run_thread 一个线程执行完, 另一个线程才能执行, 跟锁没关系。
wwqgtxx
2017-06-30 16:43:26 +08:00
@dbow gil 只是保证同时只会执行一条语句。并不是需要一个线程彻底执行完才会切换到别的线程
wwqgtxx
2017-06-30 16:44:25 +08:00
@moxiaowei 合不合理不重要,重要在于他就是这么做了
hjc4869
2017-06-30 16:57:08 +08:00
本地执行楼主的代码是 t1 t2 交替的。
moxiaowei
2017-06-30 17:01:20 +08:00
@hjc4869 没加 lock 是交替的,而且里面有错误的数据,但是我本地加上 lock 之后就不是交替执行了
dbow
2017-06-30 17:07:35 +08:00
改成这样, 应该就是顺序输出 了. 按你的写法哪个先 acuire lock 不一定。
def run_thread(n):
with lock:
code
moxiaowei
2017-06-30 17:08:34 +08:00
@dbow 好的 我来试试
moxiaowei
2017-06-30 17:09:46 +08:00
@dbow 运行结果跟我的写法是一致的
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
2017-06-30 17:19:20 +08:00
Python 的线程虽然是真正的线程,但解释器执行代码时,有一个 GIL 锁:Global Interpreter Lock,任何 Python 线程执行前,必须先获得 GIL 锁,然后,每执行 100 条字节码,解释器就自动释放 GIL 锁,让别的线程有机会执行。这个 GIL 全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在 Python 中只能交替执行



这个应该就是所谓的正解
denonw
2017-06-30 17:28:23 +08:00
虽然争夺锁应该是比较随机的,但是在 python 里面由于有 GIL,就和 @dbow 说的一样,会先执行 100 个单位才开始切换。所以原线程有很大可能又重新获得这个锁吧。
dbow
2017-06-30 17:28:40 +08:00
要是刚学 python, 会 C 的话 , 建议直接文档对着源代码看, 免得瞎猜, 浪费时间。https://github.com/python/cpython/blob/master/Python/ceval.c#L1103
moxiaowei
2017-06-30 17:29:47 +08:00
@dbow 谢谢 我是个 phper,对服务器比较感兴趣就学学 python,以后搞服务器方便
atempcode
2017-06-30 19:56:28 +08:00
多线程编程的第一原则: 不要对行程执行的先后顺序有任何的 assumption。
davinci
2017-06-30 21:52:10 +08:00
@dbow 假设在执行 90 条字节码时,线程阻塞了,此时解释器会自动释放 GIL 锁吗?
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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/372192

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX