Python:更新线程中的参数

Tom*_*mas 5 python multithreading

我想知道当该参数在程序主体中获得新值时是否可以启动一个新线程并更新其参数,所以像这样:

i = 0

def foo(i):
    print i
    time.sleep(5)

thread.start_new_thread(foo,(i,))

while True:
    i = i+1
Run Code Online (Sandbox Code Playgroud)

非常感谢您的帮助!

aba*_*ert 8

与其他任何东西一样,参数只是一个值。传递该值只是对同一值进行新的引用,如果您改变该值,每个引用都会看到它。

\n\n

全局变量和函数参数具有相同名称的事实与此无关,并且有点令人困惑,因此我将重命名其中一个。此外,您的foo函数只执行print一次(可能在您增加值之前),然后休眠 5 秒,然后完成。你可能想要一个循环;否则,你实际上无法判断事情是否正常。

\n\n

所以,这是一个例子:

\n\n
i = []\n\ndef foo(j):\n    while True:\n        print j\n        time.sleep(5)\n\nthread.start_new_thread(foo,(i,))\n\nwhile True:\n    i.append(1)\n
Run Code Online (Sandbox Code Playgroud)\n\n

那么,为什么你的代码不起作用?嗯,i = i+1不是改变值0,而是分配一个值 ,0 + 1i。该foo函数仍然具有对旧值 的引用,0该值未更改。

\n\n

由于整数是不可变的,因此您无法直接解决这个问题。但您可以非常轻松地间接解决它:用某种可变的包装器替换整数

\n\n

例如,您可以使用setget方法编写一个 IntegerHolder 类;当您i.set(i.get() + 1)和另一个引用执行此操作时i.get(),它将看到新值。

\n\n

或者您可以只使用 alist作为支架。列表是可变的,并且包含零个或多个元素。当您这样做时i[0] = i[0] + 1,它将替换i[0]为新的整数值,但i仍然是相同的列表值,这就是另一个引用所指向的内容。所以:

\n\n
i = [0]\n\ndef foo(j):\n    print j[0]\n    time.sleep(5)\n\nthread.start_new_thread(foo,(i,))\n\nwhile True:\n    i[0] = i[0]+1\n
Run Code Online (Sandbox Code Playgroud)\n\n

这可能看起来有点hacky,但它实际上是一个非常常见的Python 习惯用法。

\n\n
\n\n

同时,在另一个线程中运行的事实foo会产生另一个问题。

\n\n

理论上,线程同时运行,并且它们之间的任何数据访问没有顺序。您的主线程可能在核心 0 上运行,并处理i核心 0 缓存中的副本,而您的foo线程在核心 1 上运行,并处理核心 1 缓存中的另一个副本i,并且您的主线程中没有任何内容强制缓存同步的代码。

\n\n

在实践中,你经常会逃脱惩罚,尤其是在 CPython 中。但要真正知道何时可以摆脱它,您必须了解全局解释器锁是如何工作的,以及解释器如何处理变量,以及(在某些情况下)甚至您的平台的缓存一致性和 C 实现的内存模型如何等等在工作。所以,你不应该依赖它。正确的做法是使用某种同步机制来保护对i.

\n\n

附带说明一下,您几乎不应该使用thread代替threading,所以我也将切换它。

\n\n
i = []\nlock = threading.Lock()\n\ndef foo(j):\n    while True:\n        with lock:\n            print j[0]\n        time.sleep(5)\n\nt = threading.Thread(target=foo, args=(i,))\nt.start()\n\nwhile True:\n    with lock:\n        i[0] = i[0]+1\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

最后一件事:如果您创建了一个线程,则稍后需要join它,否则您无法干净地退出。但你的foo线程永远不会退出,所以如果你尝试退出join,你将永远阻塞。

\n\n

对于像这样的简单情况,有一个简单的解决方案。打电话之前t.start(),请执行t.daemon = True. 这意味着当您的主线程退出时,后台线程将在任意时刻自动终止。如果要写入文件或数据库,这显然是一件坏事。但就你而言,它并没有做任何持久或危险的事情。

\n\n

对于更实际的情况,您通常希望创建某种方式在两个线程之间发出信号。通常,您已经有一些东西可供线程等待\xe2\x80\x94a Queue、文件对象或它们的集合(通过select)等。如果没有,只需创建一个受锁(或条件或其他)保护的标志变量是合适的)。

\n