与其他任何东西一样,参数只是一个值。传递该值只是对同一值进行新的引用,如果您改变该值,每个引用都会看到它。
全局变量和函数参数具有相同名称的事实与此无关,并且有点令人困惑,因此我将重命名其中一个。另外,你的foo
函数仅能做到这一点print
一次(可能在您增加值之前),然后休眠 5 秒,然后完成。你可能想要一个循环;否则,你实际上无法判断事情是否正常。
所以,这是一个例子:
i = []
def foo(j):
while True:
print j
time.sleep(5)
thread.start_new_thread(foo,(i,))
while True:
i.append(1)
那么,为什么你的代码不起作用?出色地,i = i+1
不会改变值0
,它正在分配一个new value, 0 + 1
, to i
. The foo
函数仍然具有对旧值的引用,0
,不变。
由于整数是不可变的,所以你不能directly解决这个问题。但是你可以间接地解决它很容易:用某种包装器替换整数is可变的。
例如,您可以编写一个 IntegerHolder 类set
and get
方法;当你i.set(i.get() + 1)
,另一个参考文献确实如此i.get()
,它将看到新的值。
或者你可以只使用list
作为持有者。列表是可变的,并且包含零个或多个元素。当你这样做时i[0] = i[0] + 1
,这取代了i[0]
具有新的整数值,但是i
仍然是相同的列表值,这就是另一个引用所指向的。所以:
i = [0]
def foo(j):
print j[0]
time.sleep(5)
thread.start_new_thread(foo,(i,))
while True:
i[0] = i[0]+1
这可能看起来有点hacky,但它实际上是一个非常常见的Python 习惯用法。
与此同时,事实是foo
在另一个线程中运行会产生另一个问题。
理论上,线程同时运行,并且它们之间的任何数据访问没有顺序。您的主线程可能在核心 0 上运行,并处理i
那是在 core 0 的缓存中,而你的foo
线程正在核心 1 上运行,并在不同的副本上工作i
它位于核心 1 的缓存中,并且代码中没有任何内容可以强制缓存同步。
在实践中,你经常会逃脱惩罚,尤其是在 CPython 中。但实际上know当你可以逃脱它时,你必须学习全局解释器锁是如何工作的,以及解释器如何处理变量,以及(在某些情况下)甚至你的平台的缓存一致性和你的C实现的内存模型等等如何工作。所以,你不应该依赖它。正确的做法是使用某种同步机制来保护对i
.
作为旁注,你也不应该使用thread
代替threading
,所以我也将切换它。
i = []
lock = threading.Lock()
def foo(j):
while True:
with lock:
print j[0]
time.sleep(5)
t = threading.Thread(target=foo, args=(i,))
t.start()
while True:
with lock:
i[0] = i[0]+1
最后一件事:如果你创建一个线程,你需要join
稍后再做,不然你就无法彻底退出。但你的foo
线程永远不会退出,所以如果你尝试join
它,你将永远被阻止。
对于像这样的简单情况,有一个简单的解决方案。致电之前t.start()
, do t.daemon = True
。这意味着当您的主线程退出时,后台线程将在任意时刻自动终止。如果要写入文件或数据库,这显然是一件坏事。但就你而言,它并没有做任何持久或危险的事情。
对于更实际的情况,您通常希望创建某种方式在两个线程之间发出信号。通常你已经有一些东西可以让线程等待——aQueue
,一个文件对象或它们的集合(通过select
)等。如果没有,只需创建一个受锁(或条件或任何适当的内容)保护的标志变量。