我现在正在学习python3多线程并尝试测试以下代码。 代码的作用是使用多线程修改名为test_change
的函数中的全局数字balance
(初始值 = 0(,方法是首先添加然后减去相同的数字,并使用线程锁来确保balance
变量一次由一个线程更改。我循环了 100000 次或更多次以检查天气显示的预期值 0,但是,非常令人失望的是,0 并不总是我能得到的答案。这是下面的代码。我正在使用的编辑器是Vscode。
balance = 0
def test_change(n):
global balance
balance += n
balance -= n
# print("in {}, balance is {}".format(threading.current_thread().name, balance))
class MyThread(threading.Thread):
""" self-defined threading class """
def __init__(self, target_fun, fun_args, loop=False):
threading.Thread.__init__(self)
self.target_fun = target_fun
self.fun_args = fun_args
self.loop = loop
self.threadLock = threading.Lock()
def run(self): # overwrite parent run function
if not self.loop:
self.target_fun(*self.fun_args)
elif self.loop:
# with self.threadLock: # balance [15]
# for i in range(1000000):
# self.target_fun(self.fun_args)
for i in range(100000):
self.threadLock.acquire()
self.target_fun(self.fun_args)
self.threadLock.release()
nums = [5, 12]
thread_list = []
for i in range(2): # create 2 thread object
t = MyThread(target_fun=test_change, fun_args=nums[i], loop=True)
thread_list.append(t)
for i in range(len(thread_list)): # start thread object
thread_list[i].start()
for i in range(len(thread_list)): # stop main thread till subthread finishes
thread_list[i].join()
print("balance [{}]".format(balance)) # expected to be 0 always
print("subthread finishes")
这是显示的部分执行结果,您可以看到最后的输出。
(face36) D:P_project_SRSface_cumstom_dual_cam_0506> cd d:P_project_SRSface_cumstom_dual_cam_0506 &ython.exe c:Userslijin.vscodeextensionsms-python.python-2020.5.80290pythonFileslibpythondebugface_cumstom_dual_cam_0506threading_producer_consumer.py "
balance [0]
subthread finishes
(face36) D:P_project_SRSface_cumstom_dual_cam_0506> cd d:P_project_SRSface_cumstom_dual_cam_0506 &ython.exe c:Userslijin.vscodeextensionsms-python.python-2020.5.80290pythonFileslibpythondebugface_cumstom_dual_cam_0506threading_producer_consumer.py "
balance [0]
(face36) D:P_project_SRSface_cumstom_dual_cam_0506> cd d:P_project_SRSface_cumstom_dual_cam_0506 && cmd /C "C:LeeSRSPrgoramFileA_anacondaenvsface36python.exe c:Userslijin.vscodeextensionsms-python.python-2020.5.80290pythonFileslibpythondebugpyno_wheelsdebugpylauncher 2863 -- d:P_project_SRSface_cumstom_dual_cam_0506threading_producer_consumer.py "
balance [0]
subthread finishes
(face36) D:P_project_SRSface_cumstom_dual_cam_0506> cd d:P_project_SRSface_cumstom_dual_cam_0506 && cmd /C "C:LeeSRSPrgoramFileA_anacondaenvsface36python.exe c:Userslijin.vscodeextensionsms-python.python-2020.5.80290pythonFileslibpythondebugpyno_wheelsdebugpylauncher 2870 -- d:P_project_SRSface_cumstom_dual_cam_0506threading_producer_consumer.py "
balance [0]
subthread finishes
(face36) D:P_project_SRSface_cumstom_dual_cam_0506> cd d:P_project_SRSface_cumstom_dual_cam_0506 && cmd /C "C:LeeSRSPrgoramFileA_anacondaenvsface36python.exe c:Userslijin.vscodeextensionsms-python.python-2020.5.80290pythonFileslibpythondebugpyno_wheelsdebugpylauncher 3383 -- d:P_project_SRSface_cumstom_dual_cam_0506threading_producer_consumer.py "
balance [0]
subthread finishes
(face36) D:P_project_SRSface_cumstom_dual_cam_0506> cd d:P_project_SRSface_cumstom_dual_cam_0506 && cmd /C "C:LeeSRSPrgoramFileA_anacondaenvsface36python.exe c:Userslijin.vscodeextensionsms-python.python-2020.5.80290pythonFileslibpythondebugpyno_wheelsdebugpylauncher 3389 -- d:P_project_SRSface_cumstom_dual_cam_0506threading_producer_consumer.py "
balance [0]
subthread finishes
(face36) D:P_project_SRSface_cumstom_dual_cam_0506> cd d:P_project_SRSface_cumstom_dual_cam_0506 && cmd /C "C:LeeSRSPrgoramFileA_anacondaenvsface36python.exe c:Userslijin.vscodeextensionsms-python.python-2020.5.80290pythonFileslibpythondebugpyno_wheelsdebugpylauncher 3394 -- d:P_project_SRSface_cumstom_dual_cam_0506threading_producer_consumer.py "
balance [0]
subthread finishes
(face36) D:P_project_SRSface_cumstom_dual_cam_0506> cd d:P_project_SRSface_cumstom_dual_cam_0506 && cmd /C "C:LeeSRSPrgoramFileA_anacondaenvsface36python.exe c:Userslijin.vscodeextensionsms-python.python-2020.5.80290pythonFileslibpythondebugpyno_wheelsdebugpylauncher 3401 -- d:P_project_SRSface_cumstom_dual_cam_0506threading_producer_consumer.py "
balance [0]
subthread finishes
(face36) D:P_project_SRSface_cumstom_dual_cam_0506> cd d:P_project_SRSface_cumstom_dual_cam_0506 && cmd /C "C:LeeSRSPrgoramFileA_anacondaenvsface36python.exe c:Userslijin.vscodeextensionsms-python.python-2020.5.80290pythonFileslibpythondebugpyno_wheelsdebugpylauncher 3406 -- d:P_project_SRSface_cumstom_dual_cam_0506threading_producer_consumer.py "
balance [0]
subthread finishes
(face36) D:P_project_SRSface_cumstom_dual_cam_0506> cd d:P_project_SRSface_cumstom_dual_cam_0506 && cmd /C "C:LeeSRSPrgoramFileA_anacondaenvsface36python.exe c:Userslijin.vscodeextensionsms-python.python-2020.5.80290pythonFileslibpythondebugpyno_wheelsdebugpylauncher 3414 -- d:P_project_SRSface_cumstom_dual_cam_0506threading_producer_consumer.py "
balance [12] --> this is the result should never happened when using a lock.
subthread finishes
我已经尝试了两种 python 语法来启用锁,这些锁with self.threadLock:
(请参阅类运行函数中注释掉的部分(和self.threadLock.acquire(), self.threadLock.release()
,有没有人可以帮助解释这种奇怪之处。
你创建的每个线程都有自己的Lock()
实例,它们彼此无关。若要提供互斥,两个线程必须使用相同的Lock
对象。例如,在模块级别创建Lock
的单个实例,并将其传递给线程的构造函数。
按照彼得斯的建议@Tim,我修改了代码,如下所示。
import time
import threading
from threading import Thread
balance = 0
def test_change(n):
global balance
balance += n
balance -= n
print("in {}, balance is {}".format(threading.current_thread().name, balance))
class MyThread(threading.Thread):
""" self-defined threading class """
def __init__(self, lock, target_fun, fun_args, loop=False): # added a shared lock parameter
threading.Thread.__init__(self)
self.target_fun = target_fun
self.fun_args = fun_args
self.loop = loop
self.lock = lock # this is the shared lock
def run(self): # overwrite parent run function
if not self.loop:
self.target_fun(*self.fun_args)
elif self.loop:
for i in range(100):
with self.lock: # "with" after "for" so that both thread change the "balance" variable alternatively
self.target_fun(self.fun_args)
nums = [5, 12]
thread_list = []
thread_muduleLock = threading.Lock() # added a shared lock
start = time.perf_counter()
for i in range(2):
t = MyThread(thread_muduleLock, target_fun=test_change, fun_args=nums[i], loop=True) # passing the shared lock to the thread constructor
thread_list.append(t)
for i in range(len(thread_list)):
thread_list[i].start()
for i in range(len(thread_list)):
thread_list[i].join()
end = time.perf_counter()
total = end - start
print("balance [{}], total time [{}]".format(balance, total))
print("subthread finishes")
以下是执行结果:
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-7, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
in Thread-6, balance is 0
balance [0], total time [0.1192338]
subthread finishes
除了上面的那些,我尝试比较with
和acquire(), release()
语句之间的效率,发现前者比后者快一点,这是 100 次循环的结果如下。
"""with"""
for i in range(100): # with after for,
with self.lock:
self.target_fun(self.fun_args)
###### result ######
balance [0], total time [0.0022132000000000002]
subthread finishes
"""acquire & release """
for i in range(100):
self.lock.acquire()
self.target_fun(self.fun_args)
self.lock.release()
###### result ######
balance [0], total time [0.0035131000000000003]
subthread finishes