Multithreading 在TidHTTPServer.OnCommandGet内创建对象(使用计时器)失败

Multithreading 在TidHTTPServer.OnCommandGet内创建对象(使用计时器)失败,multithreading,delphi,indy10,delphi-10.3-rio,Multithreading,Delphi,Indy10,Delphi 10.3 Rio,在TidHTTPServer.OnCommandGet中,我创建了一个新对象。此对象有一个计时器,该计时器应立即启动,但不启动。时光之翼从不开火! 当我在其他地方创建对象时,它会工作 一些代码 TVolumeFader=Class(TObject) ... constructor TVolumeFader.Create(...); begin inherited Create; ... VolTimer:=TTimer.Create(NIL); VolTime

TidHTTPServer.OnCommandGet中,我创建了一个新对象。此对象有一个计时器,该计时器应立即启动,但不启动。时光之翼从不开火!
当我在其他地方创建对象时,它会工作

一些代码

  TVolumeFader=Class(TObject)

...
constructor TVolumeFader.Create(...);
begin
  inherited Create;
  ...
  
    VolTimer:=TTimer.Create(NIL);
    VolTimer.Enabled:=FALSE;
    VolTimer.Interval:=100;
    VolTimer.OnTimer:=DoTimerTick;
end;

procedure TVolumeFader.DoTimerTick(Sender:TObject);
begin
  LogWrite('TimerTick in VolumeFader',Debug);
  If Assigned(VolTimer)then Begin;
    VolTimer.Enabled:=FALSE;
  End;
  try
    LogWrite('Executing VolumeFade in VolumeFader',Debug);
    VolumeFade;
  finally
    If Assigned(VolTimer)then
      VolTimer.Enabled:=TRUE;
  end;
end;


procedure TMain.OnCommandGet;

Begin;
  TVolumeFader.Create(...);
End;

在对象的构造函数中,您正在创建
TTimer
ok,但正在将其
Enabled
属性设置为
False
。因此,请确保在构造函数退出后实际激活计时器。或者在构造函数中将
False
更改为
True

话虽如此,您的代码仍然无法如图所示工作。这是因为
TIdHTTPServer
是一个多线程组件,它的
OnCommand…
事件在
TIdHTTPServer
客户端连接到服务器时为自身创建的工作线程上下文中激发。但是
TTimer
是一个基于消息的计时器,它为自己创建一个内部
HWND
,该线程绑定到创建它的线程,并且该线程必须有一个消息循环,以便
TTimer
处理
WM_timer
消息。在中创建
TTimer
的工作线程没有消息循环,因此
TTimer
将无法触发其
OnTimer
事件

因此,您必须:

  • 创建对象后,在
    OnCommand…
    事件处理程序中运行自己的消息循环,然后在事件处理程序退出之前释放该对象。无法保证调用线程在
    OnCommand…
    事件处理程序退出后将继续运行。请注意,当计时器运行时,客户端将被阻止向服务器发送任何进一步的HTTP命令:
  • 过程TMain.OnCommandGet(AContext:TIdContext;
    ARequestInfo:TIdHTTPRequestInfo;AResponseInfo:TIdHTTPResponseInfo);
    变量
    音量控制器:TVolumeFader;
    msg:tagMSG;
    开始
    ...
    音量控制器:=TVolumeFader.Create(…);
    尝试
    当(计时器应保持运行)运行时
    开始
    //Application.ProcessMessages;
    如果peek消息(@msg,0,0,PM_REMOVE)那么
    开始
    翻译信息(@msg);
    发送消息(@msg);
    结束其他
    睡眠(100);
    结束;
    最后
    音量控制器。免费;
    结束;
    ...
    结束;
    
  • 将对象的创建及其
    TTimer
    委托给主UI线程,以便在该线程的上下文中而不是在服务器的工作线程的上下文中触发
    OnTimer
    事件处理程序。只要确保您的
    OnTimer
    代码是线程安全的,如果它需要访问与服务器共享的任何内容:
  • 过程TMain.OnCommandGet(AContext:TIdContext;
    ARequestInfo:TIdHTTPRequestInfo;AResponseInfo:TIdHTTPResponseInfo);
    开始
    ...
    TThread.Synchronize(nil、//或TThread.Queue()
    程序
    开始
    TVolumeFader.Create(…);//何时销毁此对象?
    结束
    );
    ...
    结束;
    
    它不起作用,因为您创建了它,然后立即将其设置为启用:=False
    。如果您阅读代码,则会有所帮助。:-)禁用计时器时,
    OnTimer
    事件不会发生。使用调试器一步一步地检查代码,您可以很容易地发现这些问题。您发布的代码中也有拼写错误,这说明您没有复制/粘贴自己的代码。请不要在您的帖子中重新键入代码,因为它很容易引入新的错误,可能会产生误导,或导致发布错误的答案。@KenWhite即使启用了计时器,它仍然无法如图所示工作,因为创建计时器的线程没有消息loop@RemyLebeau当前位置我错过了一个与印第岛有关的问题。但是它不能在上面用户发布的代码中工作的主要原因是它从未被启用。谢谢你的建议。同步工作完美@<代码>已启用:很抱歉缺少代码段,
    已启用
    在所有回调事件设置完毕后立即设置为“true”@<代码>销毁:对象在完成其作业后销毁自身。