【问题标题】:OmniThreadLibrary - Code: 1816. Not enough quota is available to process this commandOmniThreadLibrary - 代码:1816。没有足够的配额来处理此命令
【发布时间】:2014-08-03 10:05:44
【问题描述】:

更新 1:我包含了所有线程的堆栈跟踪,而不仅仅是主线程的 - 我认为这已经足够了。

更新 2:我重新打开了这个问题,因为即使应用了我自己问题中说明的更改,我今天仍然收到相同的错误报告...

更新 3:似乎错误发生在线程终止时,错误发生时发送的线程消息是COmniTaskMsg_Terminated。现在很奇怪 - 我已经用线程安全队列替换了程序中几乎所有对Task.Comm.Send() 的调用,所以我很确定通过Task.Comm 的线程消息数量非常少,远远少于这可能会填满 Windows 消息队列...


我在 Delphi XE4 中使用 OmniThreadLibrary-3.03a(刚刚升级到最新的 svn,我会看到...),并从最终用户那里得到一个错误报告,错误消息和线程的堆栈跟踪这个问题放在最后。

实际上,我已经减少了发生此错误的可能性,方法是将调用Task.Comm.Send() 替换为线程安全队列,后台线程使用该队列将日志发送到主线程。现在不知道在哪里看,最有可能的地方就在这里(但在这个代码块内没有Task.Comm.Send() 调用...):

  //at this point, we are already in a thread other than the main thread.
  //I've two instances of the very same thread running when the program runs.
  myTasks := Parallel.ParallelTask.NumTasks(aTaskCount);// aTaskCount = 5

  myTasks.Execute(
    //the following anonymous method will be executed in sub-threads and will have
    //multiple instances determined by the aTaskCount param.
    procedure (const aSubTask: IOmniTask)
    begin
      //note: this code block runs in multiple sub-threads in parallel.
      //aUidList can have tens of thousands (or even more) of item.
      while aUidList.Take(uid) and (not Self.task.CancellationToken.IsSignalled) do
      begin
        ThreadedDoSomething();
      end;
    end
  );//END sub-thread

堆栈跟踪:

process id         : $2f9c
allocated memory   : 181.30 MB
largest free block : 1019.73 MB
executable         : MyProgram.exe
exec. date/time    : 2014-08-02 21:12
version            : 1.0.7.256
compiled with      : Delphi XE4
madExcept version  : 4.0.9
callstack crc      : $8b5fc164, $b1225a03, $7caf0d48
exception number   : 1
exception class    : EOSError
exception message  : System Error. Code: 1816. Not enough quota is available to process this command.

thread $1160 (TOmniThread): <priority:-15>
0045c3de MyProgram.exe System.SysUtils           RaiseLastOSError
0045c35b MyProgram.exe System.SysUtils           RaiseLastOSError
0045c40f MyProgram.exe System.SysUtils           Win32Check
0075b387 MyProgram.exe OtlContainerObserver  252 TOmniContainerWindowsMessageObserverImpl.Send
0076debb MyProgram.exe OtlTaskControl       1378 TOmniTask.InternalExecute
0076db6a MyProgram.exe OtlTaskControl       1274 TOmniTask.Execute
0077423c MyProgram.exe OtlTaskControl       3091 TOmniThread.Execute
004ab243 MyProgram.exe madExcept                 HookedTThreadExecute
0053c596 MyProgram.exe System.Classes            ThreadProc
0040a5b4 MyProgram.exe System                150 ThreadWrapper
004ab129 MyProgram.exe madExcept                 CallThreadProcSafe
004ab18e MyProgram.exe madExcept                 ThreadExceptFrame
75223368 kernel32.dll                            BaseThreadInitThunk
>> created by main thread ($1408) at:
007741a1 MyProgram.exe OtlTaskControl       3080 TOmniThread.Create

main thread ($1408):
0090f52d MyProgram.exe VirtualTrees       33601 TBaseVirtualTree.SortTree
00906fd1 MyProgram.exe VirtualTrees       28348 TBaseVirtualTree.EndUpdate
008f3563 MyProgram.exe VirtualTrees       17031 TBaseVirtualTree.SetRootNodeCount
00b66d99 MyProgram.exe BackupControlFrame   219 TfraBackupControl.AddLog
00b66be2 MyProgram.exe BackupControlFrame   179 TfraBackupControl.AddLog
00b69595 MyProgram.exe BackupControlFrame   844 TfraBackupControl.TimerReadLogTimer
005f430b MyProgram.exe Vcl.ExtCtrls             TTimer.Timer
005f41ef MyProgram.exe Vcl.ExtCtrls             TTimer.WndProc
0053fca4 MyProgram.exe System.Classes           StdWndProc
76777885 USER32.dll                             DispatchMessageW
00656b87 MyProgram.exe Vcl.Forms                TApplication.ProcessMessage
00656bca MyProgram.exe Vcl.Forms                TApplication.HandleMessage
00656f05 MyProgram.exe Vcl.Forms                TApplication.Run
00b83f34 MyProgram.exe MyProgram            130 initialization
75223368 kernel32.dll                           BaseThreadInitThunk

thread $22a4:
771d0156 ntdll.dll     NtWaitForMultipleObjects
75223368 kernel32.dll  BaseThreadInitThunk

thread $1604:
771d0156 ntdll.dll                NtWaitForMultipleObjects
761215e3 KERNELBASE.dll           WaitForMultipleObjectsEx
752219f7 kernel32.dll             WaitForMultipleObjectsEx
76780864 USER32.dll               MsgWaitForMultipleObjectsEx
76780b64 USER32.dll               MsgWaitForMultipleObjects
004ab129 MyProgram.exe  madExcept CallThreadProcSafe
004ab18e MyProgram.exe  madExcept ThreadExceptFrame
75223368 kernel32.dll             BaseThreadInitThunk
>> created by main thread ($1408) at:
738778e1 gdiplus.dll

thread $3060 (TWorkerThread):
771cf8ca ntdll.dll                          NtWaitForSingleObject
76121497 KERNELBASE.dll                     WaitForSingleObjectEx
7522118f kernel32.dll                       WaitForSingleObjectEx
75221143 kernel32.dll                       WaitForSingleObject
008e2dbe MyProgram.exe  VirtualTrees   6364 TWorkerThread.Execute
004ab243 MyProgram.exe  madExcept           HookedTThreadExecute
0053c596 MyProgram.exe  System.Classes      ThreadProc
0040a5b4 MyProgram.exe  System          150 ThreadWrapper
004ab129 MyProgram.exe  madExcept           CallThreadProcSafe
004ab18e MyProgram.exe  madExcept           ThreadExceptFrame
75223368 kernel32.dll                       BaseThreadInitThunk
>> created by main thread ($1408) at:
008e2cda MyProgram.exe  VirtualTrees   6312 TWorkerThread.Create

thread $22c0:
771d1f3f ntdll.dll     NtWaitForWorkViaWorkerFactory
75223368 kernel32.dll  BaseThreadInitThunk

thread $33d0:
771cf8ca ntdll.dll                NtWaitForSingleObject
76121497 KERNELBASE.dll           WaitForSingleObjectEx
7522118f kernel32.dll             WaitForSingleObjectEx
004ab129 MyProgram.exe  madExcept CallThreadProcSafe
004ab18e MyProgram.exe  madExcept ThreadExceptFrame
75223368 kernel32.dll             BaseThreadInitThunk
>> created by thread $31fc at:
7327325b rasman.dll

thread $2dc4:
771cf8ca ntdll.dll               NtWaitForSingleObject
764b2f7b WS2_32.dll              WahReferenceContextByHandle
764b6a25 WS2_32.dll              select
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll            BaseThreadInitThunk
>> created by main thread ($1408) at:
75349791 WININET.dll

thread $2148:
771d1f3f ntdll.dll     NtWaitForWorkViaWorkerFactory
75223368 kernel32.dll  BaseThreadInitThunk

thread $2258 (TOmniThread): <priority:-15>
771d0156 ntdll.dll                          NtWaitForMultipleObjects
761215e3 KERNELBASE.dll                     WaitForMultipleObjectsEx
752219f7 kernel32.dll                       WaitForMultipleObjectsEx
76780864 USER32.dll                         MsgWaitForMultipleObjectsEx
00771942 MyProgram.exe  OtlTaskControl 2379 TOmniTaskExecutor.WaitForEvent
00770ddc MyProgram.exe  OtlTaskControl 2148 TOmniTaskExecutor.MainMessageLoop
0076fb2e MyProgram.exe  OtlTaskControl 1849 TOmniTaskExecutor.DispatchMessages
0076e7d9 MyProgram.exe  OtlTaskControl 1636 TOmniTaskExecutor.Asy_Execute
0076dd59 MyProgram.exe  OtlTaskControl 1354 TOmniTask.InternalExecute
0076db6a MyProgram.exe  OtlTaskControl 1274 TOmniTask.Execute
0077423c MyProgram.exe  OtlTaskControl 3091 TOmniThread.Execute
0040a5b4 MyProgram.exe  System          150 ThreadWrapper
004ab129 MyProgram.exe  madExcept           CallThreadProcSafe
004ab18e MyProgram.exe  madExcept           ThreadExceptFrame
75223368 kernel32.dll                       BaseThreadInitThunk
>> created by thread $1160 (TOmniThread) at:
007741a1 MyProgram.exe  OtlTaskControl 3080 TOmniThread.Create

thread $618 (TOTPWorkerThread): <priority:-15>
771d0156 ntdll.dll                          NtWaitForMultipleObjects
761215e3 KERNELBASE.dll                     WaitForMultipleObjectsEx
752219f7 kernel32.dll                       WaitForMultipleObjectsEx
752241d3 kernel32.dll                       WaitForMultipleObjects
0074b749 MyProgram.exe  DSiWin32       1949 DSiWaitForTwoObjects
007782ca MyProgram.exe  OtlComm         478 TOmniCommunicationEndpoint.ReceiveWait
007639cc MyProgram.exe  OtlThreadPool   625 TOTPWorkerThread.Execute
004ab243 MyProgram.exe  madExcept           HookedTThreadExecute
0053c596 MyProgram.exe  System.Classes      ThreadProc
0040a5b4 MyProgram.exe  System          150 ThreadWrapper
004ab129 MyProgram.exe  madExcept           CallThreadProcSafe
004ab18e MyProgram.exe  madExcept           ThreadExceptFrame
75223368 kernel32.dll                       BaseThreadInitThunk
>> created by thread $2258 (TOmniThread) at:
0076348c MyProgram.exe  OtlThreadPool   537 TOTPWorkerThread.Create

thread $5f0 (TOTPWorkerThread): <priority:-15>
771d0156 ntdll.dll                          NtWaitForMultipleObjects
761215e3 KERNELBASE.dll                     WaitForMultipleObjectsEx
752219f7 kernel32.dll                       WaitForMultipleObjectsEx
752241d3 kernel32.dll                       WaitForMultipleObjects
0074b749 MyProgram.exe  DSiWin32       1949 DSiWaitForTwoObjects
007782ca MyProgram.exe  OtlComm         478 TOmniCommunicationEndpoint.ReceiveWait
007639cc MyProgram.exe  OtlThreadPool   625 TOTPWorkerThread.Execute
004ab243 MyProgram.exe  madExcept           HookedTThreadExecute
0053c596 MyProgram.exe  System.Classes      ThreadProc
0040a5b4 MyProgram.exe  System          150 ThreadWrapper
004ab129 MyProgram.exe  madExcept           CallThreadProcSafe
004ab18e MyProgram.exe  madExcept           ThreadExceptFrame
75223368 kernel32.dll                       BaseThreadInitThunk
>> created by thread $2258 (TOmniThread) at:
0076348c MyProgram.exe  OtlThreadPool   537 TOTPWorkerThread.Create

thread $2a84 (TOTPWorkerThread): <priority:-15>
771d0156 ntdll.dll                          NtWaitForMultipleObjects
761215e3 KERNELBASE.dll                     WaitForMultipleObjectsEx
752219f7 kernel32.dll                       WaitForMultipleObjectsEx
752241d3 kernel32.dll                       WaitForMultipleObjects
0074b749 MyProgram.exe  DSiWin32       1949 DSiWaitForTwoObjects
007782ca MyProgram.exe  OtlComm         478 TOmniCommunicationEndpoint.ReceiveWait
007639cc MyProgram.exe  OtlThreadPool   625 TOTPWorkerThread.Execute
004ab243 MyProgram.exe  madExcept           HookedTThreadExecute
0053c596 MyProgram.exe  System.Classes      ThreadProc
0040a5b4 MyProgram.exe  System          150 ThreadWrapper
004ab129 MyProgram.exe  madExcept           CallThreadProcSafe
004ab18e MyProgram.exe  madExcept           ThreadExceptFrame
75223368 kernel32.dll                       BaseThreadInitThunk
>> created by thread $2258 (TOmniThread) at:
0076348c MyProgram.exe  OtlThreadPool   537 TOTPWorkerThread.Create

thread $1ca0 (TOTPWorkerThread): <priority:-15>
771d0156 ntdll.dll                          NtWaitForMultipleObjects
761215e3 KERNELBASE.dll                     WaitForMultipleObjectsEx
752219f7 kernel32.dll                       WaitForMultipleObjectsEx
752241d3 kernel32.dll                       WaitForMultipleObjects
0074b749 MyProgram.exe  DSiWin32       1949 DSiWaitForTwoObjects
007782ca MyProgram.exe  OtlComm         478 TOmniCommunicationEndpoint.ReceiveWait
007639cc MyProgram.exe  OtlThreadPool   625 TOTPWorkerThread.Execute
004ab243 MyProgram.exe  madExcept           HookedTThreadExecute
0053c596 MyProgram.exe  System.Classes      ThreadProc
0040a5b4 MyProgram.exe  System          150 ThreadWrapper
004ab129 MyProgram.exe  madExcept           CallThreadProcSafe
004ab18e MyProgram.exe  madExcept           ThreadExceptFrame
75223368 kernel32.dll                       BaseThreadInitThunk
>> created by thread $2258 (TOmniThread) at:
0076348c MyProgram.exe  OtlThreadPool   537 TOTPWorkerThread.Create

thread $3248 (TOTPWorkerThread): <priority:-15>
771d0156 ntdll.dll                          NtWaitForMultipleObjects
761215e3 KERNELBASE.dll                     WaitForMultipleObjectsEx
752219f7 kernel32.dll                       WaitForMultipleObjectsEx
752241d3 kernel32.dll                       WaitForMultipleObjects
0074b749 MyProgram.exe  DSiWin32       1949 DSiWaitForTwoObjects
007782ca MyProgram.exe  OtlComm         478 TOmniCommunicationEndpoint.ReceiveWait
007639cc MyProgram.exe  OtlThreadPool   625 TOTPWorkerThread.Execute
004ab243 MyProgram.exe  madExcept           HookedTThreadExecute
0053c596 MyProgram.exe  System.Classes      ThreadProc
0040a5b4 MyProgram.exe  System          150 ThreadWrapper
004ab129 MyProgram.exe  madExcept           CallThreadProcSafe
004ab18e MyProgram.exe  madExcept           ThreadExceptFrame
75223368 kernel32.dll                       BaseThreadInitThunk
>> created by thread $2258 (TOmniThread) at:
0076348c MyProgram.exe  OtlThreadPool   537 TOTPWorkerThread.Create

thread $11c8:
771d1f3f ntdll.dll     NtWaitForWorkViaWorkerFactory
75223368 kernel32.dll  BaseThreadInitThunk

参考this answer of another question,好像是OTL用作通信通道的Windows消息队列满了。

【问题讨论】:

  • 如果您仍在寻求帮助,我将回到我之前所说的内容,并强调您确实需要向我们展示您正在使用的可以重现问题的代码。理想情况下,您会尝试制作一个可以重现该问题的 SSCCE。 sscce.org
  • @J...,代码很复杂,并且与其他代码和特定数据相结合,我目前无法生成另一个示例代码。 PS,我刚刚添加了更新3...
  • 好吧,再一次,如果没有看到代码,很难说太多。您的下一个任务应该是复制您的源代码并开始消除代码,直到问题消失。清空方法,添加调试日志,并尽可能减少代码,直到你有一些简短的东西可以重现问题或直到问题消失。在前一种情况下,您将有更好的机会在这里获得帮助,在后一种情况下,有问题的代码应该为您识别问题。无论哪种方式,它都应该让您更接近解决方案。

标签: delphi delphi-xe4 omnithreadlibrary


【解决方案1】:

在尝试调用 PostMessage 之后,此处的 OTL 中引发了异常。

procedure TOmniContainerWindowsMessageObserverImpl.Send(aMessage: cardinal;
  wParam, lParam: integer);
begin
  Win32Check(PostMessage(cwmoHandle, aMessage, wParam, lParam));
end;

该异常表明句柄为cwmoHandle 的窗口的消息队列已满,未及时得到服务。此窗口的句柄由TOmniMessageQueue 创建,当您分配给它的OnMessage 属性时。很可能进行此分配的线程被阻塞(或阻塞时间过长)并且没有及时处理消息。

procedure TOmniMessageQueue.SetOnMessage(const value: TOmniMessageQueueMessageEvent);
begin
  if (not assigned(mqWinMsgObserver.OnMessage)) and assigned(value) then begin // set up observer
    mqWinMsgObserver.Window := DSiAllocateHWnd(WndProc);  // CREATED HERE**
    mqWinMsgObserver.Observer := CreateContainerWindowsMessageObserver(
      mqWinMsgObserver.Window, MSG_CLIENT_MESSAGE, 0, 0);
    ContainerSubject.Attach(mqWinMsgObserver.Observer, coiNotifyOnAllInserts);
    mqWinMsgObserver.Observer.Activate;
  end
  else if assigned(mqWinMsgObserver.OnMessage) and (not assigned(value)) then begin // tear down observer
    mqWinMsgObserver.Observer.Deactivate;
    ContainerSubject.Detach(mqWinMsgObserver.Observer, coiNotifyOnAllInserts);
    FreeAndNil(mqWinMsgObserver.Observer);
    DSiDeallocateHWnd(mqWinMsgObserver.Window);
  end;
  mqWinMsgObserver.OnMessage := value;
end;

要更具体地回答,我们需要查看更多代码来显示您在何处以及如何实现 OTL 对象。唯一的其他线索是您的帖子是这个;

thread $1160 (TOmniThread): <priority:-15>

这表明引发异常的线程正在以较低的优先级运行。除了您的应用程序正在积极更改线程优先级之外,这本身并不能解释任何其他内容。没有要分析的代码就没有办法说,但是您的程序结构可能会导致Priority Inversion 问题,使拥有线程处理消息的能力感到沮丧。

【讨论】:

  • 感谢您的帮助!在我提供更多详细信息之前,先快速说明一下 - 我的程序中没有代码更改任何线程的优先级,但我不确定 OTL 本身。
  • @J,请查看更新1。我会在阅读有关优先级反转的信息后进一步解释情况。
  • @EdwinYip 额外的堆栈跟踪很有帮助,但从您的程序中查看实际代码会更有帮助。您的TOmniThread 线程必须保持空闲(以与主 UI 线程相同的方式),以便它们可以处理窗口消息 - 在某个地方,很可能,必须存在阻止该处理发生的阻塞调用或循环,并且您的消息队列已满。
  • 还有一点需要注意 - 我很确定我的程序本身只使用 OTL 的消息传递子系统 (Task.Comm) 来发送非常有限数量的消息,远非如此会导致消息队列变满。
  • @J,您刚刚对问题有所了解-根据OTL作者本人的电子书-`默认情况下,ParallelTask​​在控制权返回之前等待所有后台线程完成来电者`。看起来我应该调用 NoWait() 修饰符,但是我的逻辑必须稍微改变一下……我想我很快就能得到结果。
【解决方案2】:

这是我的解决方案 - 适用于遇到同样问题的人。但我接受了@J.. 是答案,因为他帮助我找到了这个解决方案。

var
  waitEvent : IOmniCancellationToken;
begin
  // create the 'end of all sub-threads' signal
  waitEvent := CreateOmniCancellationToken;

  myTasks.NoWait.Execute(//Note the NoWait call
  procedure (const aSubTask: IOmniTask)
  begin
    doSomeDownloadTasks()
  end
  ).OnStop(procedure
  begin
    waitEvent.Signal;//once all subthreads are stopped, signal the parent thread.
  end
  );

  // wait for the sub-threads to stop, and while waiting, clear the Windows messages periodically
  while not waitEvent.IsSignalled do
  begin
    doSomeDataSavingTasks();

    //self.Task has a hidden Window handle, here we constantly empty its message queue
    //, to aovid the 'Not enough quota is available to process this command' error.
    Task.Comm.Receive(myMsg);
  end;

【讨论】:

  • 聪明地以这种方式给予信用(您正在交叉引用两个答案)。一开始我以为你不应该这样做,但是meta.stackoverflow.com/questions/256408/…
  • 这看起来很老套,像这样旋转等待其他线程停止是潜在的 CPU 占用。我认为真正的问题是为什么首先将消息添加到队列中?据我所知,OTL 无论如何都会将任何非 WM_USER 消息传递给 DefWindowProc,所以肯定只有 WM_USER 消息会导致这种情况?
  • @VincentParrett,它只是简化的代码示例,在我真实代码的 while 块中,它进行数据保存,而子线程进行下载。但是您确实提出了一个很好的问题,即使我已将这些更改应用于我的程序,我也收到了另一个相同的错误报告...现在我迷路了...也许 OTL 的作者可以提供帮助...
【解决方案3】:

我也遇到了这个问题,在我的情况下,我在从用于调试的管道阶段删除了 IOmniTaskConfig 接口后忘记删除一些 task.comm.send()

另一方面,如果出现错误,您可以调用MsgData._ReleaseAndClear; 来释放消息,因为它在TOmniCommunicationEndpoint.SendWait() 中完成。

procedure TPmOTLTaskConfigLogger.OnMessage(const ATask: IOmniTaskControl;
  const AMsg: TOmniMessage);
begin
  try
    //your code
  finally
    AMsg.MsgData._ReleaseAndClear;
  end;
end;

以下 SSCCE 使用管道引发“超出配额”异常(有时某些 AV 和“队列已满”异常)。超出的报价由otSharedInfo_ref.Monitor.Send() 中的TOmniTask.InternalExecute() 触发。

program sscce_otl_quota_exceeded_2;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  OtlCommon,
  OtlCollections,
  OtlTask,
  OtlParallel;

procedure Stage1(const input, output: IOmniBlockingCollection; const task: IOmniTask);
var
  LInputValue: TOmniValue;
begin
  for LInputValue in input do
    task.Comm.Send(1, LInputValue.AsInteger);//fill the queue
end;

var
  FPipeline: IOmniPipeline;
  LOutputValue: TOmniValue;
  i: Integer;
begin
  try
    FPipeline := Parallel.Pipeline
      //using multiple thread provokes the "Quota exceeded" exception after some
      //"Queue is full" exceptions and AVs. The "Quota exceeded" is triggered by
      //otSharedInfo_ref.Monitor.Send() in TOmniTask.InternalExecute().
      //Note: Sometimes the debugger hangs (XE3)
      .Stage(Stage1).NumTasks(16)
      .Run;

    for i := 1 to 9999 do
      FPipeline.Input.Add(i);

    FPipeline.Input.CompleteAdding;

    while not FPipeline.WaitFor(10) do
      Sleep(1);

    FPipeline := nil;
    Writeln('End');
    ReadLn;
  except
    on E: Exception do begin
      Writeln(E.ClassName, ': ', E.Message);
      ReadLn;
    end;
  end;
end.

【讨论】:

  • 我认为这里的主要问题是主线程只是阻塞了 Comm。这里需要发生两件事之一,要么 Stage1 不添加到队列中,要么在主线程中从堆栈中读取消息。紧密循环“for i := 1 to 9999 do”将是这样做的地方。另一种选择是实现一个中介,当它们准备好时,它可以在每个线程之间汇集消息。
【解决方案4】:

我们也遇到了“超出配额”和“队列已满”的例外情况。我们通过实例化我们自己的监视器并在其上调用进程消息解决了这个问题。

我们正在创建自己的任务并持有对任务控制器的引用。为此,我们将直接使用通信渠道来发送和接收消息。

//Setup 
FTaskCtrl := CreateTask(taskObj, name)
                .SetTimer(TASK_MSG_PUMP_TIMER_ID, TASK_MSG_PUMP_INTERVAL, DEBUG_MSG_PUMP)
                .OnTaskTerminated(OnTaskTerminated)
                .OnTaskMessage(OnTaskMessage);

//Sending messages to the task
FTaskCtrl.Comm.Send(CONST_MSG_NUM, TOmniValue.CastFrom<TOurMsgObj>(msgObjToSend));

//Getting messages from the task, done once every 100ms
FTaskCtrl.Comm.Receive(msgObj);

请注意,定时器消息是一个心跳消息,用于知道工作任务是否还活着。

在我们的应用程序线程或业务引擎线程的主循环中,我们将“泵送”通信以确保所有消息都得到处理。泵调用将大约每 100 毫秒调用一次。

procedure MainClass.Pump;
var
  msg : TOmniMessage;
begin
  if FTaskCtrl.Comm.Receive(msg) then
    OnTaskMessage(FTaskCtrl,msg);
end;

我们发现这会在队列中留下太多消息,因此我们将其更改为。

procedure MainClass.Pump;
var
  msg : TOmniMessage;
begin
  while FTaskCtrl.Comm.Receive(msg) do
    OnTaskMessage(FTaskCtrl,msg);
end;

这会产生突发行为。此外,我们仍然会收到终止任务的错误。关闭任务发送的特定终止消息没有被传递。

我们发现,即使我们没有创建监视器,但正在内部创建监视器。它的 wndproc 也没有被调用,或者至少没有时间处理发送给它的消息。

因此,最后我们恢复了在 Omni 线程的一些示例中实际看到的更简单的实现。

//Setup
FMonitor  := TOmniEventMonitor.Create(nil);
FMonitor.OnTaskTerminated := Self.OnTaskTerminated;
FMonitor.OnTaskMessage := Self.OnTaskMessage; 

FTaskCtrl := CreateTask(taskObj, name).SetTimer(DEBUG_TASK_MSG_PUMP_TIMER_ID, DEBUG_TASK_MSG_PUMP_INTERVAL, DEBUG_MSG_PUMP);

FTaskCtrl := FMonitor.Monitor(FTaskCtrl);


//Sending messages to the task
FTaskCtrl.Comm.Send(CONST_MSG_NUM, TOmniValue.CastFrom<TOurMsgObj>(msgObjToSend));


//Once per internal loop of the application/main logic thread. Once every 50ms.
FMonitor.ProcessMessages;

执行上述操作意味着调用了监视器 wndproc 并执行了它的所有逻辑。例如,我们看到终止消息现在得到了正确处理。

感谢 J... 最初的帖子为我们指明了正确的方向。

【讨论】:

    猜你喜欢
    • 2014-01-24
    • 1970-01-01
    • 2019-05-09
    • 1970-01-01
    • 2014-06-23
    • 1970-01-01
    • 2015-01-22
    • 2012-05-14
    • 1970-01-01
    相关资源
    最近更新 更多