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()
执行结果: t1 4661
t1 4758
t1 4856
t1 4955
t1 5055 -------------------这儿是 t1 线程已经执行完毕了
t2 109---------------------t2 线程开始执行了
t2 211
t2 314
t1 线程全部走完才会走 t2 线程
1
954880786 2017-06-30 16:23:42 +08:00 via iPhone
c 是这样的
|
5
dbow 2017-06-30 16:40:27 +08:00
CPython 有 GIL, 代码是单线程执行的, run_thread 一个线程执行完, 另一个线程才能执行, 跟锁没关系。
|
8
hjc4869 2017-06-30 16:57:08 +08:00
本地执行楼主的代码是 t1 t2 交替的。
|
10
dbow 2017-06-30 17:07:35 +08:00
改成这样, 应该就是顺序输出 了. 按你的写法哪个先 acuire lock 不一定。
def run_thread(n): with lock: code |
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; |
14
moxiaowei OP Python 的线程虽然是真正的线程,但解释器执行代码时,有一个 GIL 锁:Global Interpreter Lock,任何 Python 线程执行前,必须先获得 GIL 锁,然后,每执行 100 条字节码,解释器就自动释放 GIL 锁,让别的线程有机会执行。这个 GIL 全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在 Python 中只能交替执行
这个应该就是所谓的正解 |
15
denonw 2017-06-30 17:28:23 +08:00
虽然争夺锁应该是比较随机的,但是在 python 里面由于有 GIL,就和 @dbow 说的一样,会先执行 100 个单位才开始切换。所以原线程有很大可能又重新获得这个锁吧。
|
16
dbow 2017-06-30 17:28:40 +08:00
要是刚学 python, 会 C 的话 , 建议直接文档对着源代码看, 免得瞎猜, 浪费时间。https://github.com/python/cpython/blob/master/Python/ceval.c#L1103
|
18
atempcode 2017-06-30 19:56:28 +08:00
多线程编程的第一原则: 不要对行程执行的先后顺序有任何的 assumption。
|
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 |
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 |
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 |
24
wwqgtxx 2017-07-01 00:18:10 +08:00
@dbow 我现在就在想在直接用 ctypes 调用 c 类库的时候有没有什么办法可释放掉 GIL,因为有些时候并不想为了调用一个简单的 C 类库而去用 CPYTHON C API 写个封装,而 cytpes 类库默认应该是没有释放 GIL 的,所以会导致在一些耗时的操作上限制了多线程能力的发挥
|
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 } |