我正在尝试理解Python 中的机器翻译。我找到了一个使用
threading.Lock
的示例。但它在 Python 2.7 和 Python 3.6 中生成了不同的输出,这真的让我很困惑。
这是代码:
import threading
import time
import random
class meThread(threading.Thread):
def run(self):
global num
time.sleep(random.randint(1,3))
num += 1
print(self.name+'set num to '+str(num))
num = 0
threads = []
for i in range(5):
t = meThread()
threads.append(t)
for i in range(5):
threads[i].start()
for i in range(5):
threads[i].join()
以及Python3.6中的输出:
Thread-4set num to 1
Thread-2set num to 2
Thread-1set num to 3
Thread-3set num to 4
Thread-5set num to 5
以及Python2.7中的输出:
Thread-1set num to 1
Thread-4set num to 2
Thread-3set num to 4
Thread-2set num to 5
Thread-5set num to 3
在 3.6 中输出总是相同的,但在 2.7 中如果不使用
threading.Lock
则会出现意外。为什么? python 3.6 中会自动给线程加锁吗?
无论Python版本之间的线程行为是否发生变化,在不使用锁的情况下,跨多个不同步线程递增
num
的行为充其量是不确定的。即使在同一台 PC 上的同一个解释器上多次运行,也可能会生成不同的结果。因为你永远不知道线程上的上下文切换何时会发生。
本声明:
num += 1
只是运行时几乎与此相同的事物的简写。
REGISTER = num # read memory location into a local register
REGISTER = REGISTER + 1 # increment the value
num = REGISTER # store back to memory
并且由于任何线程都可能被另一个线程抢占或被调度到不同的核心上,或者打印调用本身可能会引入奇怪的计时问题。存在多核的缓存一致性问题。在运行时完全有可能发生这样的事情。
THREAD 1:
REGISTER = num # T1 reads 0 into register
<context switch>
THREAD 2:
REGISTER = num #T2 reads "0" into register
REGISTER = REGISTER + 1 #T2 increments register to "1"
num = REGISTER #T2 copies register value back to memory
<context switch back to thread 1, REGISTER is restored to "0" from before>
<but the memory location for num is now 1>
THREAD 1:
REGISTER = REGISTER + 1 #T1 increments register to "1"
num = REGISTER #T1 copy register value ("1") back to memory
如上所述,两个线程很容易重叠访问变量。
如果您想要
num
递增到 5 的一致行为,则需要锁定。一个简单的更新:
lock = Lock()
class meThread(threading.Thread):
def run(self):
global num
global lock
time.sleep(random.randint(1,3))
# --------------------------------
lock.acquire()
num += 1
tmp = num # save value a local tmp value for the subsequent print
lock.release()
# --------------------------------
print(self.name+'set num to '+str(tmp)) # print the value of what num was incremented to while in the lock, not what num is now
您需要了解的一切都这里。