类Memb的周期更新线程化

2024-09-26 22:10:49 发布

您现在位置:Python中文网/ 问答频道 /正文

当类中的其他方法仍在使用Python中的类成员时,更新该类成员的方法是什么?在

我希望类的其余部分继续使用成员的旧版本进行处理,直到它完全更新,然后在更新完成后将所有处理切换到新版本。在

下面是一个有趣的例子来说明我的用例,其中self.numbers是需要使用updateNumbers()中的逻辑进行安全线程化定期更新的类成员,我希望runCounter()以非阻塞方式调用它。在

from time import sleep, time

class SimpleUpdater(object):
    def __init__(self):
        self.i = 5
        self.numbers = list(range(self.i))
        self.lastUpdate = time()
        self.updateDelta = 10

    def timePast(self):
        now = time()
        delta = self.lastUpdate - now
        return (delta > self.updateDelta)

    def updateNumbers(self):
        print('Starting Update', flush=True)
        self.numbers = list(range(self.i))
        # artificial calculation time
        sleep(2)
        print('Done Updating', flush=True)

    def runCounter(self):
        for j in self.numbers:
            print(j, flush=True)
            sleep(0.5)
        self.i += 1
        if self.timePast:
            ## Spin off this calculation!! (and safely transfer the new value)
            self.updateNumbers()

if __name__ == '__main__':
    S = SimpleUpdater()
    while True:
        S.runCounter()

期望的行为是,如果self.numbers在循环中被迭代,它应该在切换到新版本之前用旧版本完成循环。在


Tags: 方法self版本truetimedef成员sleep
2条回答

您可以为对updateNumbers的每次调用创建一个新线程,但是这种类型的更常见的方法是让1个线程在后台运行一个无限循环。您应该编写一个具有无限循环的方法或函数,该方法/函数将作为后台线程的目标。这种线程通常是一个守护进程,但它不是必须的。我修改了您的代码,以展示如何实现这一点(我还修复了示例代码中的一些小错误)。在

from time import sleep, time
import threading


class Numbers(object):
    def __init__(self, numbers):
        self.data = numbers
        self.lastUpdate = time()


class SimpleUpdater(object):
    def __init__(self):
        self.i = 5
        self.updateDelta = 5
        self.numbers = Numbers(list(range(self.i)))
        self._startUpdateThread()

    def _startUpdateThread(self):
        # Only call this function once
        update_thread = threading.Thread(target=self._updateLoop)
        update_thread.daemon = True
        update_thread.start()

    def _updateLoop(self):
        print("Staring Update Thread")
        while True:
            self.updateNumbers()
            sleep(.001)

    def updateNumbers(self):
        numbers = self.numbers
        delta = time() - numbers.lastUpdate
        if delta < self.updateDelta:
            return 
        print('Starting Update')
        # artificial calculation time
        sleep(4)
        numbers = Numbers(list(range(self.i)))
        self.numbers = numbers
        print('Done Updating')

    def runCounter(self):
        # Take self.numbers once, then only use local `numbers`.
        numbers = self.numbers
        for j in numbers.data:
            print(j)
            sleep(0.5)
        # do more with numbers
        self.i += 1


if __name__ == '__main__':
    S = SimpleUpdater()
    while True:
        S.runCounter()

请注意,我没有使用任何锁:)。{{{I>在不使用任何cdi}属性的情况下,都只能使用self.numbers的每个赋值都将一个新的Numbers对象与该属性相关联。每次访问该属性时,您应该期望得到一个不同的对象,但是如果您在方法的开头对该对象进行本地引用,即numbers = self.numbersnumbers将始终引用同一个对象(直到它在方法末尾超出作用域),即使后台线程更新了self.numbers。在

我创建一个Numbers类的原因是,我可以在单个(原子)赋值(数字列表和lastUpdated值)中获取和设置所有“volatile”成员。我知道这有很多需要跟踪的地方,老实说,使用锁可能更聪明、更安全:),但我也希望向您展示这个选项。在

创建一个锁以控制对列表的访问:

import threading

def __init__(self, ...):
    # We could use Lock, but RLock is somewhat more intuitive in a few ways that might
    # matter when your requirements change or when you need to debug things.
    self.numberLock = threading.RLock()
    ...

当你需要保存一个本地列表时,保存一个本地变量。局部变量不会受实例属性更新的影响;请使用局部变量,直到要检查更新的值:

^{pr2}$

每当您需要更新列表时,请按住锁并用新列表替换该列表,而不要更改旧列表:

with self.numberLock:
    self.numbers = newNumbers

顺便说一下,我在这里使用camelcase来匹配您的代码,但是Python的惯例是使用lowercase_with_underscores而不是{}作为变量和函数名。在

相关问题 更多 >

    热门问题