【问题标题】:Acceptable use of Thread.Sleep()Thread.Sleep() 的可接受使用
【发布时间】:2012-01-11 00:42:58
【问题描述】:

我正在开发一个控制台应用程序,它将按设定的时间间隔安排和运行,比如每 30 分钟一次。它的唯一目的是查询 Web 服务以更新一批数据库行。

Web 服务 API 建议每 30 秒调用一次,并在设定的时间间隔后超时。下面以伪代码为例:

listId := updateList(<list of terms>)
LOOP
  WHILE NOT isUpdatingComplete(listId)
END LOOP
statuses := getStatuses(“LIST_ID = {listId}”)

我在 C# 中大致将其编码为:

int callCount = 0;
while( callCount < 5 && !client.isUpdateComplete(listId, out messages) )
{
    listId = client.updateList(options, terms, out messages);
    callCount++;
    Thread.Sleep(30000);
}
// Get resulting status...

在这种情况下可以使用 Thread.Sleep() 吗?我知道这通常不是好的做法,但从阅读不使用它的原因来看,这似乎是可以接受的用法。

谢谢。

【问题讨论】:

  • 您对Thread.Sleep 的使用完全可以接受
  • 控制台应用程序会被调度吗?如果是这样,为什么不把它安排为每 30 分钟一次呢?更好的解决方案是使用计时器调用方法的 Windows 服务。
  • 为什么不呢?不使用的原因是什么?
  • @Ray:看起来这个特定的代码只需要 2.5 分钟即可运行(5 个循环,迭代之间等待 30 秒)。听起来这些循环计划每 30 分钟执行一次。换句话说,循环不会坐 30 分钟..
  • @Ray 我认为控制台应用程序计划每 30 分钟运行一次与 Web 服务的 API 建议调用者每隔 30 秒进行一次重试尝试之间存在混淆。在这种情况下,看起来 Thread.Sleep 被用于间隔重试,而不是作为调度控制台应用程序工作的机制。控制台应用程序每 30 分钟运行一次这一事实与此无关。

标签: c# sleep


【解决方案1】:

Thread.Sleep 确保当前线程在至少指定的毫秒数过去之前不会返回。有很多地方适合这样做,假设它在后台线程上运行,您的示例似乎很好。

您不想使用它的一些示例位置 - 在 UI 线程上或您需要进行精确计时的地方。

【讨论】:

  • 我认为您的资格“假设它在后台线程上运行”是可以的,但在这种情况下,应用程序似乎是一个控制台应用程序,将作为计划任务自动运行,所以我认为在这种情况下,没有必要确保它在后台线程上运行。如果这是一个交互式应用程序,那么我完全同意你的资格。
  • 我的意思是后台线程由任何在后台运行的线程,即不是 UI 线程。服务线程我会考虑后台线程。 (我不是指线程对象上的IsBackground,它只是指线程在应用程序退出时如何被销毁/清理,IIRC)。哎呀,没看到你说控制台。是的,这是有争议的术语,但它并不是真正的背景线程,非常正确。然后我可能会争辩说控制台应用程序不太合适..
  • 啊,我明白你的意思了。 :) 我无意争论语义,但您的评论让我意识到,在这种情况下,控制台应用程序的“主线程”可以被视为“后台”线程,因为没有用户交互。
【解决方案2】:

一般来说,Thread.Sleep 就像任何其他工具一样:完全可以使用,除非它被严重滥用。我不同意“通常不是好的做法”部分,这是人们在应该做其他事情(即阻塞同步对象)时滥用Thread.Sleep 的结果。

在您的情况下,程序是单线程的,它没有 UI(即线程没有消息循环)并且您不想与外部事件同步。所以Thread.Sleep就好了。

【讨论】:

    【解决方案3】:

    对 Sleep() 的普遍反对意见是它浪费了一个线程。

    在您的情况下,只有 1 个线程(也许 2 个),所以这不是真正的问题。

    所以我认为它看起来不错(但我会睡 29 秒以减少一些懈怠)。

    【讨论】:

    • 我不相信 29 秒的事情。亚马逊在一些 API 调用之间需要 60 秒。 Thread.Sleep(60 * 1000) 在极少数情况下仍然会出现关于 60 秒延迟的错误,因此,在 那种特定情况下,我会睡 62 秒,即让它更多松弛。 YMMV 等等……
    【解决方案4】:

    没关系,只是一旦它进入睡眠状态就不能中断它,而不中止线程(不推荐)。

    这就是为什么ManualResetEvent 可能是一个更好的主意,因为它可以从不同的线程发出信号(“唤醒”)。

    【讨论】:

      【解决方案5】:

      您可以坚持使用 Thread.Sleep 方法。但是将其安排为每 30 分钟运行一次会更优雅 - 这样您就不必担心应用程序内部的等待。

      【讨论】:

        【解决方案6】:

        Thread.Sleep 不是执行周期性逻辑的最佳选择。 Thread.Sleep(n) 意味着您的线程将放弃控制 n 毫秒。无法保证它会在 n 毫秒后重新获得控制权,这取决于 CPU 负载。

        【讨论】:

          【解决方案7】:

          如果您将线程锁定 30 分钟,您应该每 30 分钟安排一次 Windows 任务,以便程序执行然后结束。这样你就不会长时间锁定线程。

          对于较短的时间,例如 30 秒/1 分钟,System.Thread.Sleep() 非常好。超过 5 分钟,我会使用 Windows 任务。 (我是西班牙语,我认为英文版是这样称呼的,我在谈论您从控制面板安排的任务;-))

          【讨论】:

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