全局变量:在线程读取前,其值会改变吗?
在下面的代码中,你会看到 pickledList
被线程使用,并且它是在全局范围内设置的。
如果线程使用的变量是在最后的 while 循环中动态设置的,那么在线程使用这个变量之前,它的值有可能会改变吗?我该如何在循环中动态设置一个值,然后把它传给线程,并确保这个值在线程使用之前不会改变呢?
import pickle
import Queue
import socket
import threading
someList = [ 1, 2, 7, 9, 0 ]
pickledList = pickle.dumps ( someList )
class ClientThread ( threading.Thread ):
def run ( self ):
while True:
client = clientPool.get()
if client != None:
print 'Received connection:', client [ 1 ] [ 0 ]
client [ 0 ].send ( pickledList )
for x in xrange ( 10 ):
print client [ 0 ].recv ( 1024 )
client [ 0 ].close()
print 'Closed connection:', client [ 1 ] [ 0 ]
clientPool = Queue.Queue ( 0 )
for x in xrange ( 2 ):
ClientThread().start()
server = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
server.bind ( ( '', 2727 ) )
server.listen ( 5 )
while True:
clientPool.put ( server.accept() )
编辑:
这里有一个更好的例子来说明我的问题。如果你运行这个,有时候在线程输出之前,值会改变,导致有些值被跳过:
from threading import Thread
class t ( Thread ):
def run(self):
print "(from thread) ",
print i
for i in range(1, 50):
print i
t().start()
我该如何将 i
的值传递给线程,以便它不再与这个变量绑定,这样即使 i
中存储的值发生变化,线程正在使用的值也不会受到影响。
1 个回答
run(arg1, arg2, kwarg1="three times!")
选项 1:你可以在每次创建线程时传递参数:
ClientThread(arg1, arg2, kwarg1="three times!").start()
在这种情况下,当你调用 start()
时,线程实例会调用你的 run
方法。
如果你需要把可变对象(比如字典、列表、实例)传给这个函数,你必须确保重新赋值给全局变量,而不是直接修改它。
选项 2:你可以在你的 ClientThread
对象上设置一个实例变量:
myThread.setMyAttribute('new value')
使用选项 2 时,你需要注意竞争条件等问题,这取决于方法的具体操作。每当你从线程中写入或读取数据时,使用 Lock
是个好主意。
选项 3:在第一次调用 run
时获取全局变量,并在本地存储一份副本:
run(self):
localVar = globalVar # only for immutable types
localList = globalList[:] # copy of a list
localDict = globalDict.copy() # Warning! Shallow copy only!
如果线程在运行期间不需要改变传入的值,选项 1 是正确的选择;如果需要改变,选项 2 更合适。选项 3 则是一种变通方法。
关于在 Python 中传递变量或值,你需要记住,不可变对象(字符串、数字、元组)是通过值传递的,而可变对象(字典、列表、类实例)是通过引用传递的。
因此,修改字符串、数字或元组不会影响之前传递过这些变量的实例,但修改字典、列表或实例则会。将一个变量重新赋值为另一个对象不会影响之前使用旧值的任何东西。
全局变量不应该用于可能会改变的值(如果有的话)。基本上,你的例子做法是错误的。