Jump to content
bill parish

Omnithread DELPHI, many calls in the same function

Recommended Posts

Delphi10.2.3: OTL, FMX, ACTIVEX DCOM

i have a big problem in my code to call the parallel.Async many times i have 6 ActiveX DCOM in Delphi, and wanna call these in same time, to calculate an invoice, so in my exe i work with OTL to async all calls activex, i don't like to wait result or freeze my screen.

 

Parallel.Async(
  procedure (const task: IOmniTask)
  var iInnerExc: integer;
  begin
       /// ***********************************************************///
       { return Log}
       TLogSingleton.Instance().Log('Services XX calcul: Début appel.',True);
       //************************* PARALLEL ASYNC ****************************//
       // executed in background thread
       try //
            {Create instance de ViewModel annexes: XXServiceXX}
            mainViewModelPreCalculOTLLevel03 := CreateMainViewModelOTLLevel03Class;
            {Affecter le même Model principal}
            mainViewModelPreCalculOTLLevel03.Model := fModel;
            {Suscribe the observer in list}
            mainViewModelPreCalculOTLLevel03.Provider.Subscribe(fSubscriber);

            {Call VieModel.ServiceXX}
            mainViewModelPreCalculOTLLevel03.StartPrecalcul;
       except on e:exception do
        {call the raise, juste ci dessous, dans le onterminated Handle}
        raise Exception.Create('Exception lors du traitement de Service XX.');
       end;

  end,
Parallel.TaskConfig.OnTerminated(
  procedure (const task: IOmniTaskControl)
  var excp: Exception;
  begin
        // executed in main thread
        if assigned(task.FatalException) then
        begin
          {expoiler l'exception, captcher ci dessus}
          excp := task.DetachException;
          TErrorSingleton.Instance().Log(Format('Service XX: [Fin TASK Error]. %s:%s',[excp.ClassName, excp.Message]),True);
          FreeAndNil(excp);
        end
        else
        begin
          // executed in main thread
          { return Log}
          TLogSingleton.Instance().Log('Services calcul: [Fin TASK] Fin appel traitement.',True);
          SendNotification([actServiceXXFinish]);
        end;
  end
));

the Active X Call in exe FMX: (the server activeX is integreted in framework, i don't have the source code, and i don't know haw it writen)

 

 _Dm_Dcom := CreateObject('SERVICE.XX.CLT.SERVICEXX', 'DEFAUT') as ICoServiceXX; //Instance of the DLL
    //CALL the function in ServiceXX_impl
    _Dm_Dcom.GETTRaitementServiceXX(SafeArrayIn,SafeArrayOut,FListnerForPushNotif );
//with SafeArrayIn =Json IN, and take result in SafeArrayOut; FListnerForPushNotif to send notification (Type Interface: Iunknow)

the ActiveX initialization in every SERVICEXX (DLL)

TAutoObjectFactory.Create(ComServer, TTest, Class_Test, ciMultiInstance, tmApartment);

in this DLL; we have ServiceXX_impl unit, we have one function to call traitement in another class, we can found a communication with the IInterface (published) to send notif like this:

(FListnerForPushNotif as IDataListnerForPushNotification).PushNotificationForLog('Message to send to client side');
//we have many logs types: event, Error, Exception
//PushNotificationForLog, PushNotificationForError,PushNotificationForException

this is my call, i repeat it 6 times,

 

Parallel.Async(
        CALL01
);
Parallel.Async(
        CALL01
);
Parallel.Async(
        CALL02
);

Parallel.Async(
        CALL06
);

i don't need to freeze my exe, and i don't need to wait, with

SendNotification([actServiceXXFinish]); or other Action like "actService01Finish", "actService02Finish"  

i can kown if my traitement is finished or not,

finaly, if i write all 6 calls, i will fall in many and many problems, like freeze screen, and deadlock (the only solution kill the process crl+alt+supp), but if i write only one call, it will be perfect, i have, my result, but the freeze screen persist,

Conclusion:

1- with many calls: problems: screen freeze, traitement not working, --> kill the process.

2- with one only call: problems: screen freeze: the "TAniIndicator" not working and the screen freeze when Delphi enter in the other DLL (activeX).

Nota: the Exe is FMX (in another step it will be a Form application), the 6 services are DLL (ActiveX DCOM).

plz help me

Edited by bill parish
orthographe

Share this post


Link to post
1 hour ago, Primož Gabrijelčič said:

I'm very sorry but I really don't speak any Frech. Can you please translate your question?

it's ok now, 🙂 

Share this post


Link to post

This is actually a COM problem, I believe, and I know almost nothing about COM, so please consider that my thinking may be entirely wrong.

 

believe that you are having problems because you initialize your server in tmApartment mode but then access it from multiple threads. http://docwiki.embarcadero.com/RADStudio/Sydney/en/Choosing_a_Threading_Model says for tmApartment: " All client calls use the thread in which the object was created." And the object is created in the main thread.

 

Does your code work if you use TThread instead of OTL to run async tasks?

Share this post


Link to post

I realise this is an old post...

 

The other thing - usually with COM, you need to CoInitialize() in each thread, and if you are a nice citizen, CoUninitialize().

 

To coordinate shutdown, you may also want to look at TCountdownEvent from System.SyncObjs. TCountdownEvent waits for a counter to reach zero. A signal in a thread would trigger the countdown.

 

Could do something like (sorry, I automatically referenced TTask.Run rather than Parallel.Async) 

// This is pseudocode, so  might not be 100% accurate with params
var event := TCountdownEvent.Create();
event.AddCount(length(workList));
for var work in workList do
begin
   TTask.Run(procedure 
   begin
      CoInitialize();
        try
			process(work);
     	finally
           CoUninitalize();
			event.signal(); 
		end;
   end;
end;
event.WaitFor(INFINITE);

With the above code, if run from a UI would still block, so that should also be threaded if the UI is meant to remain responsive.

Share this post


Link to post
9 minutes ago, darnocian said:

The other thing - usually with COM, you need to CoInitialize() in each thread, and if you are a nice citizen, CoUninitialize().

This could be pretty expensive if the task is short lived.

 

As a general rule, the owner of the thread should take charge of initialising COM. In this case it's probably the thread pool. I'd be looking for a feature of the threadpool whereby the client could request that threads created by the thread pool come initialised for COM. Then the initialisation can happen once for each thread, rather than for each task.

Share this post


Link to post

Performance wise 100% agree with you David. It was more an example to check it out initialisation in case that was an issue.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×