Python 3 中的线程模块有变化吗?如果是这样,怎么办?

问题描述 投票:0回答:1

我正在尝试理解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 multithreading
1个回答
2
投票

无论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

您需要了解的一切都这里

© www.soinside.com 2019 - 2024. All rights reserved.