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_long_string/otl_long_unicode_string类