Python:更新线程中的参数 [英] Python: update argument in thread

查看:68
本文介绍了Python:更新线程中的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否可以启动一个新线程并在此参数在程序的主程序中获得新值时更新其参数,例如:

I was wondering if it would be possible to start a new thread and update its argument when this argument gets a new value in the main of the program, so something like this:

i = 0

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

thread.start_new_thread(foo,(i,))

while True:
    i = i+1

非常感谢您的帮助!

推荐答案

参数只是一个值,就像其他任何东西一样.传递值只是对同一个值产生一个新的引用,如果你改变那个值,每个引用都会看到它.

An argument is just a value, like anything else. Passing the value just makes a new reference to the same value, and if you mutate that value, every reference will see it.

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

The fact that both the global variable and the function parameter have the same name isn't relevant here, and is a little confusing, so I'm going to rename one of them. Also, your foo function only does that print once (possibly before you even increment the value), then sleeps for 5 seconds, then finishes. You probably wanted a loop there; otherwise, you can't actually tell whether things are working or not.

所以,这是一个例子:

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 的值,而是分配了一个 值,0 + 1,到i.foo 函数仍然引用了旧值 0,它没有改变.

So, why doesn't your code work? Well, i = i+1 isn't mutating the value 0, it's assigning a new value, 0 + 1, to i. The foo function still has a reference to the old value, 0, which is unchanged.

由于整数是不可变的,你不能直接解决这个问题.但是你可以很容易地间接解决它:用某种可变的包装器替换整数.

Since integers are immutable, you can't directly solve this problem. But you can indirectly solve it very easily: replace the integer with some kind of wrapper that is mutable.

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

For example, you can write an IntegerHolder class with set and get methods; when you i.set(i.get() + 1), and the other reference does i.get(), it will see the new value.

或者您可以只使用 list 作为持有人.列表是可变的,并且包含零个或多个元素.当您执行 i[0] = i[0] + 1 时,会用新的整数值替换 i[0],但 i仍然是相同的列表值,这就是另一个引用所指向的.所以:

Or you can just use a list as a holder. Lists are mutable, and hold zero or more elements. When you do i[0] = i[0] + 1, that replaces i[0] with a new integer value, but i is still the same list value, and that's what the other reference is pointing at. So:

i = [0]

def foo(j):
    print j[0]
    time.sleep(5)

thread.start_new_thread(foo,(i,))

while True:
    i[0] = i[0]+1

这可能看起来有点老套,但它实际上是一个非常常见的 Python 习语.

This may seem a little hacky, but it's actually a pretty common Python idiom.

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

Meanwhile, the fact that foo is running in another thread creates another problem.

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

In theory, threads run simultaneously, and there's no ordering of any data accesses between them. Your main thread could be running on core 0, and working on a copy of i that's in core 0's cache, while your foo thread is running on core 1, and working on a different copy of i that's in core 1's cache, and there is nothing in your code to force the caches to get synchronized.

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

In practice, you will often get away with this, especially in CPython. But to actually know when you can get away with it, you have to learn how the Global Interpreter Lock works, and how the interpreter handles variables, and (in some cases) even how your platform's cache coherency and your C implementation's memory model and so on work. So, you shouldn't rely on it. The right thing to do is to use some kind of synchronization mechanism to guard access to i.

作为旁注,你也应该几乎从不使用 thread 而不是 threading,所以我也将切换它.

As a side note, you should also almost never use thread instead of threading, so I'm going to switch that as well.

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

<小时>

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


One last thing: If you create a thread, you need to join it later, or you can't quit cleanly. But your foo thread never exits, so if you try to join it, you'll just block forever.

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

For simple cases like this, there's a simple solution. Before calling t.start(), do t.daemon = True. This means when your main thread quits, the background thread will be automatically killed at some arbitrary point. That's obviously a bad thing if it's, say, writing to a file or a database. But in your case, it's not doing anything persistent or dangerous.

对于更现实的情况,您通常希望创建某种方式在两个线程之间发送信号.通常你已经有一些东西让线程等待——一个Queue、一个文件对象或它们的集合(通过select)等.如果没有,只需创建由锁(或条件或任何适当的东西)保护的标志变量.

For more realistic cases, you generally want to create some way to signal between the two threads. Often you've already got something for the thread to wait on—a Queue, a file object or collection of them (via select), etc. If not, just create a flag variable protected by a lock (or condition or whatever is appropriate).

这篇关于Python:更新线程中的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆