【问题标题】:Alternative to sleep inside a thread替代在线程内睡眠
【发布时间】:2016-03-01 20:19:30
【问题描述】:

各种答案表明在线程中休眠是一个坏主意,例如:Avoid sleep。为什么?经常给出的一个原因是,如果线程处于休眠状态,则很难优雅地退出线程(通过发出终止信号)。

假设我想定期检查网络文件夹中的新文件,可能每 10 秒检查一次。这对于优先级设置为低(或最低)的线程来说似乎是完美的,因为我不希望潜在的耗时文件 I/O 影响我的主线程。

有哪些选择?代码在 Delphi 中给出,但同样适用于任何多线程应用程序:

procedure TNetFilesThrd.Execute();
begin
    try
        while (not Terminated) do
            begin
            // Check for new files
            // ...

            // Rest a little before spinning around again
            if (not Terminated) then
                Sleep(TenSeconds);
            end;
    finally
        // Terminated (or exception) so free all resources...
    end;
end;

一个小的修改可能是:

// Rest a little before spinning around again
nSleepCounter := 0;
while (not Terminated) and (nSleepCounter < 500) do
    begin
    Sleep(TwentyMilliseconds);
    Inc(nSleepCounter);
    end;

但这仍然涉及睡眠...

【问题讨论】:

  • 在一个线程中休眠也可能很危险,并且在使用多线程时会导致未定义的行为。
  • 更好的选择是等待信号事件相同的超时时间;如果事件发出信号,则立即退出线程,如果超时,则留在 while 循环中。
  • @ITguy 睡觉没有内在的危险,也不会导致未定义的行为
  • 解决方案在 C# 和 C++ 中会完全不同,在 C++ 中它甚至可能不在 Windows 上,这个问题在 C# 和 C++ 中被标记是愚蠢的。编辑问题。阿兰,让你的问题笼统是愚蠢的。同样愚蠢的问题可能是,我该如何做晚餐。 (说明应该适用于任何晚餐。)
  • @Warren 通用问题很好。答案在任何地方都是一样的。

标签: c# c++ multithreading delphi


【解决方案1】:

执行此操作的标准方法是等待取消事件。在如下所示的伪代码中:

while not Terminated do
begin
  // Check for new files
  // ...

  // Rest a little before spinning around again
  FTerminationEvent.WaitFor(TenSeconds);
end;

为了终止你将覆盖TerminatedSet:

procedure TMyThread.TerminatedSet;
begin
  inherited;
  FTerminationEvent.SetEvent; // abandon the wait in the thread method
end;

对事件的等待要么超时,要么因事件发出信号而终止。这可以让您的线程暂停一段时间而不会给 CPU 带来负担,同时还能保持对终止请求的响应。

【讨论】:

  • 您知道 FTerminationEvent 的 Delphi-7 单元(或 D7 等效单元)是什么吗?我正忙着在谷歌上搜索试图找到它,但此刻正在跑下兔子洞!...
  • @AlainD 你需要标准的 TEvent,FTerminationEvent 只是变量名来理解这个事件代表什么。
  • @YuriyAfanasenkov:啊,好的,谢谢!现在已经找到这篇文章 (stackoverflow.com/questions/1734644/…) 并正在关注它。将对其进行测试并尽快报告。
  • 您不能覆盖OnTerminate,因为这是一个事件。我真的不想评论你的代码,我看不到。您以一般的方式提出了这个问题,与任何特定的语言无关。因此,有关您的实施的详细信息超出了您设置的范围。我认为你有这个问题的答案。如果您在实施时遇到问题,那就另当别论了。
  • 方法TThread.DoTerminateTThread.Execute方法完成后调用,它的主要目的是调用TThread.OnTerminated事件。最好的选择(较新的 Delphi 版本)是覆盖 TThread.TerminatedSet 方法(但 Delphi 7 不知道)
【解决方案2】:

如果这是我的工作,我想我会用一个包含 TTimer 的包装类来解决它,每 10 秒产生一个新线程。

生成一个新线程的成本有点高,但如果你只每 10 秒执行一次,我认为对主线程的性能影响可以忽略不计。

步骤:

  1. 创建一个包装类 TMyFileSearcher。
  2. 让它包含一个 TTimer。
  3. 每次计时器到达时,都会产生一个新线程并搜索文件。
  4. 向 TMyFileSearcher 添加 OnTerminate 处理程序,以处理返回的文件。

还有其他一些注意事项,例如跟踪是否已生成线程,这样您就不会在旧线程运行时创建新线程。

但是,除此之外,我认为实施起来应该非常简单。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    相关资源
    最近更新 更多