我有无限数量的条目正在通过网络界面输入。在每分钟的基础上,我想将最后一小时收到的元素转储到一个适当命名的文件中 (datetime.now().strftime('%Y_%m_%d_%H_%M' )
).
到目前为止,这是我的设计:
线程 1
不断接收输入并添加到 data_dict
结构:{datetime.now().strftime('%Y_%m_%d_%H_%M') : []}
线程 2
休眠一分钟并写入 data_dict[(datetime.now() - timedelta(minutes=1)).strftime('%Y_%m_%d_%H_%M')]
问题
- 以这种方式使用 dict 是线程安全的吗?
- 这是一个好的设计吗? :)
最佳答案
1) 这是(几乎)线程安全的。 dict 上的各个操作是线程安全的,您的读取线程永远不应该从仍在写入的键中读取。异常(exception)情况是以下竞争条件,它依赖于接近分钟边界发生的上下文切换。
线程 1:在 2014-05-20 13:37:59.999 收到一条消息,然后被抢占
线程 2:检查时间(现在是 2014-05-20 13:38:00.000)所以它从 2014_05_20_13_37 开始读取
线程 1:将其消息附加到 2014_05_20_13_37 队列的末尾
2) 不,这不是好的设计,不仅仅是因为线程安全条件中存在边缘情况。如果你需要保证每一分钟都做某事, sleep 是一种非常容易出错的方法。首先, sleep 操作不会在给定的时间内完全 sleep 。它至少睡了那么长的时间。其次,即使它是准确的,您的其余操作仍然需要一些时间,这意味着您的 sleep 调用之间会有毫秒数的漂移。这两个因素可能会导致您每 6000-60000 分钟就错过一分钟。
忽略第 1 部分中的竞争条件,我将执行以下操作:
def generate_times():
now = datetime.datetime.now()
next_time = datetime.datetime(now.year, now.month, now.day, now.hour, now.minute)
while True:
yield next_time
next_time += datetime.timedelta(minute=1)
def past_times():
for time in generate_times():
while time > datetime.datetime.now() - datetime.timedelta(minute=1):
time.sleep(1.0)
yield time
第一个函数创建一个生成器,生成所有的分钟时间,第二个函数确保时间已经过去。
处理第一部分中的竞争条件的最简单方法可能是让线程 2 滞后 2 分钟,而不是仅滞后 1 分钟(或者 1 分 7 秒或 16 分钟,无论您想要什么)。这仍然不是万无一失的:如果你有一些东西让你的内核停滞很长时间,那么这些竞争条件仍然可能发生,但这是一个完美的 Storm 场景。
如果你想 100% 正确,那么线程 1 需要保持一个时间戳来跟踪它写出日志的最新时间,但是你将要研究非阻塞 IO 以确保你的最后一次如果没有线程第一个被卡住等待记录,日志不会停止。我,我自己,只会在 past_times 函数中使用 2 分钟而不是 1 分钟,除非这是生命所依赖的东西的关键任务日志记录。
关于python - 以周期性间隔记录无限数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23797195/