【发布时间】:2013-06-05 20:47:44
【问题描述】:
一般概述
- 我有一个中等规模的 django 项目
- 我在内存中有一堆前缀树(而不是 DB)
- 这些树的节点表示受到超时影响的实体/对象。即,我需要在不同的时间点使这些节点超时
设计:
- 本质上,我需要一个 Timer 构造,它允许我触发一个可重置的 1-shot 计时器并关联并给它一个回调,该回调可以对创建计时器的实体执行一些操作,在本例中是树。
在浏览了各种选项后,我找不到任何可以原生使用的东西(比如一些 django 应用程序)。 Python 中的 Timer 对象不适合这种情况,因为它不会扩展/执行。因此,我决定根据以下内容编写自己的计时器:
- 包含时间范围的时间增量对象的排序列表
- 触发“tick”的机制
实施选择:
- 在 Bisect 周围使用了一个包装器,用于排序的 delta 列表: http://code.activestate.com/recipes/577197-sortedcollection/
- 使用 celery 提供滴答声 - 1 分钟的粒度,工作人员将触发我的 Timer 类提供的 timer_tick 函数。 timer_tick 本质上应该通过排序列表,每次滴答都会减少头节点。然后,如果有任何节点的记号为 0,则启动回调并将这些节点从排序的计时器列表中删除。
- 创建计时器涉及实例化一个返回对象 ID 的 Timer 对象。此 id 存储在 db 中,并与 DB 中的条目相关联,该条目表示创建计时器的实体
其他数据结构: 为了跟踪 Timer 实例(为每个计时器创建实例化),我有一个 WeakRef 字典,它将 id 映射到 obj
所以本质上,我的主要 Django 项目的内存中有 2 个数据结构。
问题陈述:
由于 celery worker 需要遍历计时器列表并且还可能修改 id2obj 映射,看来我需要找到一种方法在我的 celery worker 和 main 之间共享状态
通过 SO/Google,我发现以下建议
- 经理
- 共享内存
不幸的是,bisect wrapper 不适合酸洗和/或状态共享。我通过创建一个字典并尝试将排序列表嵌入到字典中来尝试管理器方法。它出现了一个错误(我猜是因为排序列表持有的内存没有共享并将其嵌入到“共享”内存对象将不起作用)
最后...问题:
- 有什么方法可以与工作线程共享我的 SortedCollection 和 Weakref Dict
替代解决方案:
如何保持工作线程简单...让它在每个滴答时写入 DB,然后使用 post Db 信号在主线程上获得通知并在主线程中执行过期计时器的处理。当然,缺点是我失去了并行性。
【问题讨论】:
-
老兄,这是一篇博文还是一个问题?难以置信的大!
-
旁注:我认为使用搜索树(例如,来自
bintrees或blist的内容)而不是带有bisect的列表会好很多。使用这种设计,每次添加/删除/过期计时器时,都是 O(N) 操作。 -
更重要的是:“bisect wrapper 不适合酸洗和/或状态共享”是什么意思?它只是一个列表,可能带有一些额外的状态变量,这对于腌制或共享来说是微不足道的——或者更简单的是,使用
multiprocessing.Array而不是list。 -
此外,您在谈论线程和进程之间来回切换。他们不是一回事。使用两个线程,默认情况下共享所有内容;您所要做的就是添加适当的同步。对于两个进程,默认情况下不会共享任何内容;您必须明确传递或分享它。
-
最后,如果你试图在正确隔离的进程之间共享某些东西,并且对于插入、删除、任意查找和查找最低值大约为 O(log N) 或更好……听起来像数据库的工作。有理由不在这里使用吗?
标签: python django timer django-celery celerybeat