定期记录无限数据

时间:2014-05-22 03:07:34

标签: python multithreading

我有无数的条目通过网络界面提供。在每分钟的基础上,我想将过去一小时收到的元素转储到一个名称相应的文件(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')]

的内容

问题

  1. 以这种方式使用dict线程安全吗?
  2. 这是一个好设计吗? :)

4 个答案:

答案 0 :(得分:2)

考虑使用外部存储 - redis将是我的选择,并且由于它独立于您的应用程序而运行,因此您可以避免任何线程问题。另外,redis对于这类东西来说很快。

答案 1 :(得分:2)

我不喜欢睡眠(60)和timedelta(分钟= 1)的耦合。我认为你可能会得到时间上的不准确性,随着时间的推移会导致跳过输出中写入的基准。

我宁愿利用两个事实:

  1. 在写文件
  2. 之前等到分钟结束是可以的
  3. 时间只向前移动,所以一旦完成,你就知道数据实际上是不可变的
  4. 考虑到这一点,您知道该分钟的输入完成之后的时间,然后您可以使用最后60分钟的数据写入文件。你的第二个线程只是睡觉,直到这个条件为真。然后它会唤醒,将条件更改为下一分钟,处理数据,然后返回等待条件再次触发。实质上,您刚刚编写了一个简单的队列,在分钟边界上同步。

答案 2 :(得分:2)

1)这(几乎)线程安全。 dict上的各个操作都是线程安全的,你的读取线程永远不应该从仍在写入的键中读取。例外情况是以下竞争条件,它依赖于接近一分钟边界的上下文切换。

主题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)不,这不是好设计,不仅仅是因为在线程安全条件下存在边缘情况。如果你需要保证每分钟都有一些东西,睡觉是一种非常容易出错的方法。首先,睡眠操作不能完全睡眠时间给定的时间。它至少睡了那么多时间。其次,即使它是准确的,你的其余操作仍然需要一些时间,这意味着你的睡眠呼叫之间会有几毫秒的漂移。这两个因素可能会导致你每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分(或者一分钟,7分钟或16分钟,无论你想要什么)。这仍然不是故障保护:如果你有一些东西会使你的内核长时间停滞,那么这些竞争条件仍然会发生,但这是一个完美的风暴场景。

如果你想100%正确,那么线程1需要保持一个时间戳跟踪它写出日志的最新时间,但是你会想要查看非阻塞IO以确保你的最后一次如果没有任何东西是线程一个等待记录的东西,则log不会停止。我,我自己,只会在past_times函数内部使用2分钟而不是1,除非这是生命依赖的任务关键日志记录。

答案 3 :(得分:1)

看看这个答案: Thread Safety in Python's dictionary

您也可以编写单独的流程。我已经使用uwsgi来设置每个线程可以与之通信的实例,并使用http调用发布数据。可能不是最有效的,但它非常简单且非常安全。