【问题标题】:How to choose a random time once per hour如何每小时选择一次随机时间
【发布时间】:2013-02-12 00:58:14
【问题描述】:

假设我想每小时运行一次任务,但每小时运行一次。它不一定是真正随机的。例如,我只是不想在每小时的最开始做这件事。而且我只想每小时执行一次。

这消除了几种明显的方法,例如随机睡 30 到 90 分钟,然后再睡。该任务有可能(并且很有可能)连续运行多次,睡眠时间不超过 30 分钟。

我正在考虑的方法是这样的:每隔一小时,对一小时的 Unix 时间戳进行哈希处理,然后将结果修改为 3600。将结果添加到一小时的 Unix 时间戳,这就是任务的时刻应该运行。在伪代码中:

while now = clock.tick; do
  // now = a unix timestamp
  hour = now - now % 3600;
  hash = md5sum(hour);
  the_time = hour + hash % 3600;
  if now == the_time; then
    do_the_work();
  end
end

我确信这会满足我的要求,但我认为把这个问题抛出来看看其他人有什么想法会很有趣!

【问题讨论】:

  • 但是明天上午 9:00 与今天上午 9:00 的 Unix 时间戳不同,因此会有不同的输入。我忘了说我使用的是 Unix 时间戳。将更新问题。
  • 我不确定我是否完全按照。如果总持续时间是例如10 小时,您希望任务恰好运行 10 次?
  • 好问题。我看我并没有真正陈述一个假设。这是一个每秒循环一次的长时间运行(永远运行?)的程序。它每秒钟都在做大量的工作。每小时一次,但在一个可变的时间,我希望它做另一个小任务。
  • 我突然想到,我可以使用小时的时间戳作为 rand() 的种子,而不是使用哈希函数。结果仍然是可预测的,但可能具有更好的分布,具体取决于散列函数。

标签: random time


【解决方案1】:

对于接下来的一小时工作,只需在该小时内随机选择一分钟。

也就是说,为下一个间隔选择一个随机时间进行工作;如果工作已从上一个间隔结转,则这可能与当前间隔(小时)相同。

“睡觉的时间”就是在那之前的时间。如果随机时间在现在之前,这也可以在结转情况下“立即”执行:这将确保每个小时选择一个随机时间,除非工作需要超过一个小时。

不要让它变得比它必须的更复杂 - 没有理由在这里用random 散列或以其他方式捣乱。这就是像 SharePoint 计时器(带有每小时计划)这样的“企业”解决方案的工作原理。

【讨论】:

  • 我同意,但事实上这允许任务在失败的情况下重新运行。例如,如果进程重新启动并丢失了它所做的选择。使用现成的技术可以使选择相当耐用,但这很复杂,并且不能绝对保证耐用(问我怎么知道!)我实际上并不认为我的想法比你的想法更复杂,尽管请不要'不要把它当作对你的想法的批评。这篇文章的重点是激发创意:)
  • 另一方面,如果想要一个不可预测的时间选择,你的想法显然更好。我的想法选择了一个可变但完全可预测的时间。
  • @BaronSchwartz 我只是在使用我所知道的计时器/作业间隔。对于处理错误恢复情况,“ACID”存储工作得很好,尽管这些约束可能可以放宽到像哨兵文件或控制容器这样简单的东西。我不完全确定为什么从公式得出的可预测但非恒定的时间会比“每小时 20 分钟”(这本身就是指定下一次时间的一种有效方式)有益。
  • 我想对一些数据进行采样,但我并不总是想同时进行,以免得到倾斜的视图。例如,如果有一个例行任务在每小时 20 分钟运行,而我总是在那个时候对我的数据进行采样,我最终会相信该任务一直在运行。
【解决方案2】:
  1. 安排您的任务(使用 cron 等)在每小时的最开始运行。

  2. 在您的任务开始时,随机休眠一段时间,从 0 到 (60 - (您的任务的预计运行时间 + 一个软糖因素)) 分钟。

  3. 如果您不希望您的任务同时运行两次,您可以使用 pid 文件。该任务可以在睡眠后检查此文件并等待当前运行的任务完成后再重新开始。

【讨论】:

  • 我对这个想法和 pst 的想法有同样的看法;处理错误情况有一些内在的复杂性。不过是个好主意。
【解决方案3】:

我已经部署了我建议的解决方案,并且运行良好。例如,每分钟一次,我从我正在监视的进程中采样一些信息,但我在一分钟内的不同时间进行采样。我在 Go 代码中创建了一个 Timestamp 类型的方法,叫做 RandomlyWithin,如下:

func (t Timestamp) RandomlyWithin(dur Timestamp, entropy ...uint32) Timestamp {
    intervalStart := t - t % dur
    toHash := uint32(intervalStart)
    if len(entropy) > 0 {
        toHash += entropy[0]
    }
    md5hasher.Reset()
    md5hasher.Write([]byte{
        uint8(toHash >> 24 & 255),
        uint8(toHash >> 16 & 255),
        uint8(toHash >> 8 & 255),
        uint8(toHash & 255)})
    randomNum := binary.BigEndian.Uint32(md5hasher.Sum(nil)[0:4])
    result := intervalStart + Timestamp(randomNum)%dur
    return result
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多