I am using TOmniBlockingCollection
from a "Server Thread".
In this thread, I am hosting a Single-Threaded Apartment COM object to a DataProvider (CoInitialize()
and CoUninitialize()
are called).
Then additionally, I am using multiple worker threads to write to an ADO Dataset (TADOCommand
), which also needs CoInitialize()/CoUninitialize()
.
Only when I catch an error from inside the WorkerFunction
and try to terminate the "Server Thread", I am waiting (infinitely?) on the CoUninitialize()
in the "Server Thread" (inherited from TThread
) that started the WorkerFunction
.
Callstack:
:774546bc ntdll.ZwWaitForAlertByThreadId + 0xc
.... some other system functions
:771e8069 combase.CoUninitialize + 0xf9
UTServerThread.TServerThread.Execute (calls CoUninitialize)
How do I avoid the wait in the Server Thread when I catch an error ... it seems to be something COM-related in multi-threading.
Pseudo Code:
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()
. For this reason, I tend to NOT write code directly betweenCoInitialize
/CoUninitialize
, but instead move the code inside of a procedure called betweenCoInitialize
/CoUninitialize
, so that all local/temporary variables are finalized on exit, eg:procedure DoTaskWork; begin ... end; procedure WorkerFunction(...); begin CoInitialize(nil); try DoTaskWork; finally CoUninitialize; end; end;
– Remy LebeauCoInitializeEx(nil,COINIT_MULTITHREADED)
instead ofCoInitialize(nil)
? – Xalo