【发布时间】: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)进行检查?
标签: multithreading delphi com omnithreadlibrary