OTL TOmniBlockingCollection(COM 多线程)中的 CoInitialize/CoUninitialize 错误处理

Posted

技术标签:

【中文标题】OTL TOmniBlockingCollection(COM 多线程)中的 CoInitialize/CoUninitialize 错误处理【英文标题】:CoInitialize/CoUninitialize error handling in OTL TOmniBlockingCollection (COM multi-threading) 【发布时间】:2021-10-30 20:51:32 【问题描述】:

我正在使用来自“服务器线程”的TOmniBlockingCollection

在这个线程中,我将一个单线程单元 COM 对象托管到一个 DataProvider(CoInitialize()CoUninitialize() 被调用)。

此外,我正在使用多个工作线程写入 ADO 数据集 (TADOCommand),这也需要 CoInitialize()/CoUninitialize()

只有当我从WorkerFunction 内部捕获错误并尝试终止“服务器线程”时,我才在“服务器线程”(继承自TThread)中的CoUninitialize() 上等待(无限期?)这开始了WorkerFunction

调用栈:

:774546bc ntdll.ZwWaitForAlertByThreadId + 0xc 
.... some other system functions
:771e8069 combase.CoUninitialize + 0xf9
UTServerThread.TServerThread.Execute (calls CoUninitialize)

当我发现错误时如何避免服务器线程中的等待...这似乎与多线程中的 COM 相关。

伪代码:

procedure CreateIOmniWorkers;
begin
  for Each IOmniWorker in OmniWorkerArry
  begin
    IOmniWorker := CreateTask("WorkerFunction" as TOmniTaskMethod, Description)
    .SetParameter('Input', InputCollection)
    .WithLock(TSynchroObject.Create) // The Used Logger inside Worker Funtion needs a Lock
    .OnTerminated(ErrorHandler);
  end;
end;

procedure ErrorHandler(const task: IOmniTaskControl);
var
  eException: Exception;
begin
  if Assigned(task.FatalException) then
  begin
    eException := task.DetachException;
    try
    Logger.Error(
      'TOTLBlockingListWorker', eException.ClassName,
      Format(rsScriptErrorDataBlock,[eException.Message, eException.StackTrace]));
    finally
      FreeAndNil(eException);
    end;
  end;
end;

procedure WorkerFunction(const task: IOmniTask; var SyncData: TSyncOutputValueHolder);
begin
  CoInitialize(nil);
  try
    CreateConnection(SyncData);
    // Somewhere here a Exception happens
    WriteSQLStyle(task, SyncData);
  finally
    CoUninitialize; // is called
  end;
end;

procedure UTServerThread.TServerThread.Execute
begin
  CoInitialize(nil);
  try
    while not Terminated
    begin
      CreateIOmniWorkers;
      with TOTLBlockingCollection do
       while DataAvailable
         TOTLBlockingCollection.Add(Datapackage);
       TOTLBlockingCollection.CompleteAdding;
       while not TOTLBlockingCollection.IsFinalized
         Sleep(250);
    end;
  finally
    CoUninitialize; // Here the (infinite?) Wait happens.
  end;
end;

【问题讨论】:

确保在调用CoUninitialize() 之前 正确释放所有COM 接口,尤其是隐式/临时接口。出于这个原因,我倾向于不在CoInitialize/CoUninitialize 之间直接编写代码,而是将代码移动到在CoInitialize/CoUninitialize 之间调用的过程中,以便所有本地/临时变量在退出时完成,例如:procedure DoTaskWork; begin ... end; procedure WorkerFunction(...); begin CoInitialize(nil); try DoTaskWork; finally CoUninitialize; end; end; 您是否使用CoInitializeEx(nil,COINIT_MULTITHREADED) 而不是CoInitialize(nil) 进行检查? 【参考方案1】:

我还遇到了 BlockingList 的其他问题。

如果在 TryTake 内部发生异常,直到阻塞列表的 False 循环不会释放接口对象。

所以我确实将我的 OTL 源更新到了当前版本(3.07.8 是 07.6)。 CoInitialize 和 Unitialize 的问题已经消失了……但是……我必须在合成中仍然存在错误。 代替 OnTerminadted 处理程序,我将监视器附加到我的 CreateIOmniWorkers,但我没有在那里收到事件。 OnTerminated 处理程序也不会触发。所以它有效......但我不相信我做对了。

repeat
  if not BlockingList.TryTake(Params) then
    break; // simplified

    InterfacedObject := TInferfacedObject.Create as ISomething
    try
      raise Exception while having a TInterfacedObject here ...
    Finally
      InterfaceObject := nil; // if this finally is here in case of Error the Interface would not be freed.
    end;
until false

【讨论】:

以上是关于OTL TOmniBlockingCollection(COM 多线程)中的 CoInitialize/CoUninitialize 错误处理的主要内容,如果未能解决你的问题,请参考以下文章

OTL翻译 -- OTL的主要类

OTL翻译 -- otl_stream类

OTL4.0 otl_stream 绑定问题

OTL翻译 -- otl_long_string/otl_long_unicode_string类

无法解析符号 otl_connect(otl_connect 类不包括)

otl翻译(11) -- OTL的迭代器