在Delphi中编程延迟的最好方法是什么? [英] What is the best way to program a delay in Delphi?

查看:378
本文介绍了在Delphi中编程延迟的最好方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发的一个Delphi应用程序必须延迟一个,有时是两秒钟。我想使用最佳做法对这个延迟进行编程。在阅读有关Delphi的Sleepover()方法的Stackoverflow中,我发现了这两个意见:


我按照这个格言:如果你觉得需要使用Sleep(),你做错了。 - Nick Hodges Mar 12 '12 at 1:36



@nick的确。我的等同是睡眠是没有问题的解决方案。 - David Heffernan Mar 12 '12 at 8:04


关于Sleep()的评论



为了回应这个建议,以避免调用Sleep(),以及对于使用Delphi的TTimer和TEvent类的理解,我已经编写了以下原型。我的问题是:


  1. 这是一个适当的方法来计划延迟吗?

  2. 如果答案是的,那么为什么这比呼唤Sleep()更好?






  type 
TForm1 = class(TForm)
Timer1:TTimer;
procedure FormCreate(Sender:TObject);
程序Timer1Timer(Sender:TObject);

private
public
EventManager:TEvent;

end;

TDoSomething = class(TThread)

public
procedure Execute;覆盖
程序延迟;
结束

var
Form1:TForm1;
东西:TDoSomething;

执行

{$ R * .dfm}

程序TDoSomething.Execute;
var
i:integer;

begin
FreeOnTerminate:= true;
Form1.Timer1.Interval:= 2000; // 2秒间隔延迟2秒
Form1.EventManager:= TEvent.Create;
为i:= 1到10 do
begin
延迟;
writeln(TimeToStr(GetTime));
结束
FreeAndNil(Form1.EventManager);
结束

程序TDoSomething.Delay;
begin
//使用TTimer与TEvent实例协同实现延迟。
Form1.Timer1.Enabled = = true;
Form1.EventManager.ResetEvent;
Form1.EventManager.WaitFor(INFINITE);
Form1.Timer1.Enabled:= false;
结束

procedure TForm1.FormCreate(Sender:TObject);
begin
Something:= TDoSomething.Create;
结束

程序TForm1.Timer1Timer(Sender:TObject);
begin
//时间到了。结束延迟
EventManager.SetEvent;
结束


解决方案

依次回答您的问题:


  1. 这是否是一种延迟计划的正确方法?

是(但也是否 - 见下文)。



正确的方式根据具体要求和问题解决而有所不同。没有任何通用真相,任何人告诉你,否则都是试图卖给你一些东西(再释义)。



在某些情况下等待事件是适当的延迟机制。


  1. 如果答案是肯定的,那么为什么这比调用Sleep()更好?

见上文:答案是。然而,这个第二个问题根本没有意义,因为它假定 Sleep()总是和永远不是正确的方法,正如在#不一定是这种情况。



睡眠()可能不是编程延迟的最佳或最适当的方式>所有情景,但有些情况是最实际的,没有明显的缺陷。



为什么人们避免睡眠()ing



睡眠()是一个潜在的问题,正是因为它是一个无条件的延迟,直到特定时间段过去才能中断。替代延迟机制通常实现完全相同的事情,唯一的区别是除了只是时间流逝之外,还有一些替代机制来恢复执行。



等待事件延迟直到事件发生(或被破坏)一段特定的时间过去。



等待互斥锁导致延迟,直到互斥体被获取(或被销毁)一段时间已过。



等。



换句话说:虽然一些延迟机制是可中断的。 睡眠()不是。但是,如果您遇到其他机制错误,仍然有可能引入重大问题,而且往往可能难以识别。



问题With Event.WaitFor()在这种情况下



问题中的原型突出显示了使用任何挂起的机制的潜在问题如果该代码的其余部分没有以与该特定方法兼容的方式执行,则执行代码

  Form1.Timer1.Enabled:= true; 
Form1.EventManager.ResetEvent;
Form1.EventManager.WaitFor(INFINITE);

如果这个代码在主线程中执行,那么 Timer1 永远不会发生



问题中的原型在一个线程中执行,所以这个特殊的问题不会出现,但是值得探索的潜力,因为原型确实引入了一个不同的由于此线程的参与而导致问题。



通过在 WaitFor() INFINITE 等待超时>在事件中,您暂停线程的执行,直到该事件发生。 TTimer 组件使用基于Windows消息的计时器机制,当计时器过去时,将向您的消息队列提供 WM_TIMER 消息。要发生 WM_TIMER 消息,您的应用程序必须处理其消息队列。



还可以创建Windows计时器,它将提供回调另一个线程,这可能是一个更合适的方法在这个(诚然是人为的)情况。然而,这不是VCL TTimer 组件提供的功能(至少XE4,我注意到您正在使用XE2)。



问题#1



如上所述, WM_TIMER 消息依赖于您的应用程序处理其消息队列。您已经指定了2秒钟的计时器,但是如果您的应用程序进程忙于执行其他工作,那么该消息可能需要2秒钟的时间才能处理。



值得一提的是这里是 Sleep()也有一些不准确的情况 - 它确保线程在指定的时间段内至少暂停,但不保证正确指定的延迟。



问题#2



原型设计了一种使用定时器和事件延迟2秒的机制,可以通过简单的调用 Sleep()来实现几乎完全相同的结果。



这个和一个简单的 Sleep()调用之间的唯一区别是,如果正在等待的事件被销毁,你的线程也将恢复。



然而,在一个现实世界的情况下,一些进一步的处理遵循延迟,这是itel f如果不正确处理,这是一个潜在的重大问题。在原型中,这种不可靠性根本无法满足。即使在这种简单的情况下,很可能如果事件被破坏,那么线程尝试禁用的 Timer1 也是如此。



Caveat Developor 可能会导致线程中出现访问冲突 strong>



从语法上避免使用 Sleep()不能正确理解所有的线程同步机制(其延迟只是一个)以及操作系统本身工作的方式,以便每种场合都可以部署正确的技术。



事实上,在您的原型的情况下, Sleep()可以说是更好的解决方案(如果可靠性是关键指标),因为该技术的简单性确保您的代码将在2秒后恢复,而不会陷入等待不必要的陷阱过于复杂(相对于现在的问题)技术。



说完了,这个原型显然是一个有创意的例子。



根据我的经验非常少的实际情况,其中 Sleep()是最佳解决方案,尽管它往往是最简单的最不容易出错的情况。但我永远不会说永远不会。


A Delphi application that I'm working on must delay for one, or sometimes two, second(s). I want to program this delay using the best practices. In reading entries about Delphi's Sleep() method on stackoverflow, I found these two comments:

I live by this maxim: "If you feel the need to use Sleep(), you are doing it wrong." – Nick Hodges Mar 12 '12 at 1:36

@nick Indeed. My equivalent is "There are no problems for which Sleep is the solution." – David Heffernan Mar 12 '12 at 8:04

comments about Sleep()

In response to this advice to avoid calling Sleep(), along with my understanding about using Delphi's TTimer and TEvent classes, I have programmed the following prototype. My questions are:

  1. Is this a proper way to program a delay?
  2. If the answer is yes, then why is this better than a call to Sleep()?


type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);

  private
  public
    EventManager: TEvent;

  end;

  TDoSomething = class(TThread)

  public
    procedure Execute; override;
    procedure Delay;
  end;

var
  Form1: TForm1;
  Something: TDoSomething;

implementation

{$R *.dfm}

procedure TDoSomething.Execute;
var
  i: integer;

begin
  FreeOnTerminate := true;
  Form1.Timer1.Interval := 2000;       // 2 second interval for a 2 second delay
  Form1.EventManager := TEvent.Create;
  for i := 1 to 10 do
    begin
      Delay;
      writeln(TimeToStr(GetTime));
    end;
  FreeAndNil(Form1.EventManager);
end;

procedure TDoSomething.Delay;
begin
  // Use a TTimer in concert with an instance of TEvent to implement a delay.
  Form1.Timer1.Enabled := true;
  Form1.EventManager.ResetEvent;
  Form1.EventManager.WaitFor(INFINITE);
  Form1.Timer1.Enabled := false;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Something := TDoSomething.Create;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  // Time is up.  End the delay.
  EventManager.SetEvent;
end;

解决方案

Taking your questions in turn:

  1. Is this a proper way to program a delay?

Yes (but also "no" - see below).

The 'proper way' varies according to the specific requirements and the problem being solved. There is no Universal Truth on this and anyone telling you otherwise is trying to sell you something (to paraphrase).

In some cases waiting on an event is the proper delay mechanism. In other cases not.

  1. If the answer is yes, then why is this better than a call to Sleep()?

See above: The answer is yes. However, this second question simply does not make sense since it assumes that Sleep() is always and by necessity never the proper way which, as is explained in the answer to #1 above, is not necessarily the case.

Sleep() may not be the best or most appropriate way to program a delay in all scenarios, but there are scenarios where it is the most practical and has no significant drawbacks.

Why People Avoid Sleep()ing

Sleep() is a potential problem precisely because it is an unconditional delay that cannot be interrupted until a specific time period has elapsed. Alternative delay mechanisms typically achieve precisely the same thing with the only difference being that there exists some alternative mechanism to resume execution, other than the mere passage of time.

Waiting for an event delays until the event occurs (or is destroyed) or a specific period of time has passed.

Waiting for an mutex causes a delay until the mutex is acquired (or is destroyed) or a specific period of time has passed.

etc.

In other words: Whilst some delay mechanisms are interruptible. Sleep() is not. But if you get the other mechanisms wrong there is still the potential to introduce significant problems and often in a way that can be far more difficult to identify.

Problems With Event.WaitFor() In This Case

The prototype in the question highlights a potential problem of using any mechanism that suspends execution of your code if the rest of that code is not implemented in a way that is compatible with that particular approach:

 Form1.Timer1.Enabled := true;
 Form1.EventManager.ResetEvent;
 Form1.EventManager.WaitFor(INFINITE);

If this code is executed in the main thread then Timer1 will never happen.

The prototype in the question executes this in a thread, so this particular problem doesn't arise, but it is worth exploring the potential since the prototype does introduce a different problem as a result of the involvement of this thread.

By specifying an INFINITE wait timeout on your WaitFor() on the event, you suspend execution of the thread until that event occurs. The TTimer component uses the windows message based timer mechanism, in which a WM_TIMER message is supplied to your message queue when the timer has elapsed. For the WM_TIMER message to occur, your application must be processing its message queue.

Windows timers can also be created which will provide a callback on another thread, which might be a more appropriate approach in this (admittedly artificial) case. However this is not a capability offered by the VCL TTimer component (as of XE4 at least, and I note you are using XE2).

Problem #1

As noted above, WM_TIMER messages rely on your application processing its message queue. You have specified a 2 second timer but if your application process is busy doing other work it could potentially take far longer than 2 seconds for that message to be processed.

Worth mentioning here is that Sleep() is also subject to some inaccuracy - it ensures that a thread is suspended for at least the specified period of time, it does not guarantee exactly the specified delay.

Problem #2

The prototype contrives a mechanism to delay for 2 seconds using a timer and an event to achieve almost exactly the same result that could have been achieved with a simple call to Sleep().

The only difference between this and a simple Sleep() call is that your thread will also resume if the event it is waiting for is destroyed.

However, in a real-world situation where some further processing follows the delay, this is itself a potentially significant problem if not correctly handled. In the prototype this eventuality is not catered for at all. Even in this simple case it is most likely that if the event has been destroyed then so too has the Timer1 that the thread attempts to disable. An Access Violation is likely to occur in the thread as a result when it attempts to disable that timer.

Caveat Developor

Dogmatically avoiding the use of Sleep() is no substitute for properly understanding all thread synchronization mechanisms (of which delays are just one) and the way in which the operating system itself works, in order that the correct technique may be deployed as each occasion demands.

In fact, in the case of your prototype, Sleep() provides arguably the "better" solution (if reliability is the key metric) since the simplicity of that technique ensures that your code will resume after 2 seconds without falling into the pitfalls that await the unwary with over-complicated (with respect to the problem at hand) techniques.

Having said that, this prototype is clearly a contrived example.

In my experience there are very few practical situations where Sleep() is the optimal solution, though it is often the simplest least error prone. But I would never say never.

这篇关于在Delphi中编程延迟的最好方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆