1

我有一个更新全局/类变量的函数。那么,定期调用子线程等函数后应该注意什么?(以异步方式)

或者,有什么建议可以避免使用这种模式?(悲惨的方式)

import time
import threading

# through global variable or class variable
_a = 123


def update_a():        # may be called more than once
    "slow updating process"
    time.sleep(3)
    global _a
    _a += 10
    return

if __name__ == '__main__':
    print(_a)
    th = threading.Thread(target=update_a)
    th.setDaemon(True)
    th.start()
    print(_a)
    # updating aynchrounously
    time.sleep(5)
    print(_a)
4

2 回答 2

1

这表明添加不是线程安全的(请参阅 Josiah Carlson 的评论。effbot.org现在似乎已关闭;您可以通过此处的回路机器查看该页面的存档版本。):

import threading
x = 0
def foo():
    global x
    for i in xrange(1000000):
        x += 1
threads = [threading.Thread(target=foo), threading.Thread(target=foo)]
for t in threads:
    t.daemon = True
    t.start()
for t in threads:
    t.join()
print(x)

产生一些小于 2000000 的数字。这表明某些调用x += 1没有正确更新变量。

解决方案是用锁保护对全局变量的分配:

lock = threading.Lock()
def safe_foo():
    global x
    for i in xrange(1000000):
        with lock:
            x += 1

x = 0
threads = [threading.Thread(target=safe_foo), threading.Thread(target=safe_foo)]
for t in threads:
    t.daemon = True
    t.start()
for t in threads:
    t.join()
print(x)

产量 2000000。

于 2013-06-25T14:16:56.103 回答
1

首先,线程在 Python 中是完全要避免的,但如果你真的想这样做,我会这样做。首先,创建一个带锁的线程安全对象:

class ThreadSafeValue(object):
    def __init__(self, init):
        self._value = init
        self._lock = threading.Lock()

    def atomic_update(self, func):
        with self._lock:
            self._value = func(self._value)

    @property
    def value(self):
        return self._value

然后我将它传递给线程目标函数:

def update(val):
    time.sleep(3)
    val.atomic_update(lambda v: v + 10)

def main():
    a = ThreadSaveValue(123)
    print a.value
    th = threading.Thread(target=update, args=(a,))
    th.daemon = True
    th.start()
    print a.value
    th.join()
    print a.value

if __name__ == '__main__':
    main()

这样您将避免全局变量并确保线程安全。

于 2013-06-25T14:18:04.300 回答