0

I have a Delphi (Windows) application created using Delphi 10 that has some blocking calls that I would like to convert to using threads. Unfortunately for me, these are not procedures, but functions. (And information on how to return function results from threads appears to be much more limited.) I am trying to familiarize myself with the OmniThreadLibrary, since it seems to be the most flexible and best supported threading library for Delphi, but I'm having trouble with understanding how to do this.

I have been able to get the various OmniThreadLibrary routines to work well with procedures, but when I try to set up a function, I get an error about capturing the result. When I use OmniThreadLibrary's Future example as a starting point, I can get the function to work, but I can't figure out how to connect to the event monitor, how to send messages from the task, etc. So, it seems as if I'm overlooking something no matter which way I try to solve this problem.

Currently, my program does something like this:

If myPing(IPAddress) then
begin
  //Do other things here…
end;

Because myPing is blocking, and I actually need it to wait until myPing returns true before processing further, the application gets sluggish during this process. I'd like to put the myPing call in a thread, which would solve the sluggishness problem, but I can't figure out how to do that in the form of a function using OmniThreadLibrary. (Unless I use a future, in which case I can't figure out how to connect to the Event Monitor.)

Edit 1: Since my original post, I have made a little progress. I was able to connect the Event Monitor to the Future by adding Parallel.TaskConfig.MonitorWith(Form1.OmniEventMonitor1) to my code, right after the function. However, I still can't figure out how to send messages to that event monitor from within the Future function.

Edit 2: I now have some sample code. My first attempt was similar to this:

function myPing(HostName: string): IOmniFuture<boolean>;
begin
  Result := Parallel.Future<boolean>(function: boolean
  begin
    Result := False;
    //Do actual ping here...  Set Result := True if successful.
  end
  );
end;

The basic function worked, but did not allow me to send any messages to the TOmniEventMonitor. I was able to get that part working by changing the code to this:

function myPing(HostName: string): IOmniFuture<boolean>;
begin
  Result := Parallel.Future<boolean>(function: boolean
  begin
    Result := False;
    //Do actual ping here...  Set Result := True if successful.
  end,
  Parallel.TaskConfig.MonitorWith(Form1.OmniEventMonitor1)
  );
end;

Now, I can successfully monitor the OnTaskTerminated event, but I still can't send messages to the Event Monitor from the task. By changing my code once again, I can access the task itself and send messages using task.Comm.Send(), but the messages don't reach the EventMonitor:

function myPing(HostName: string): IOmniFuture<boolean>;
begin
  Result := Parallel.Future<boolean>(function(const task: IOmniTask): boolean
  begin
    Result := False;
    //Do actual ping here...  Set Result := True if successful.
    task.Comm.Send(0,'Test 1');
  end,
  Parallel.TaskConfig.MonitorWith(Form1.OmniEventMonitor1)
  );
end;
Various60
  • 1
  • 2
  • One way would be to do your pinging using OMNI Thread parallel for and if pinging of certain IP is successful you create a new job for doing "other stuff" where you pass the IP adress to that job as a starting parameter. – SilverWarior Feb 22 '17 at 07:24
  • 1
    Also you might want to check the paralel pipeline example http://www.thedelphigeek.com/2010/11/multistage-processes-with.html – SilverWarior Feb 22 '17 at 07:28
  • Which version of Delphi? Which platform (Windows or mobile)? – Olaf Hess Feb 22 '17 at 12:04
  • I'm using Delphi 10 Berlin (Windows) for this project. – Various60 Feb 22 '17 at 12:35

1 Answers1

0

Here's a simple example on how to retrieve the function result from the async call. It does not use an "OmniEventMonitor" but instead calls a function once the async call returns ("Ping" is defined in PingU.pas, but not of importance here):

unit MainFormU;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Menus;

type
  TPingResultEvent = procedure (const bResult: Boolean) of object;

  TOnTerminateTestForm = class(TForm)
    LogMemo: TMemo;
    MainMenu: TMainMenu;
    PingMenu: TMenuItem;

    procedure PingMenuClick(Sender: TObject);

  private
    procedure BackgroundPing (const sServer: String;
                              const OnResult: TPingResultEvent);
    procedure PingResult (const bResult: Boolean);

  public
    { Public declarations }
  end;

var
  OnTerminateTestForm: TOnTerminateTestForm;

implementation

{$R *.dfm}

uses PingU, OtlParallel, OtlTaskControl;

procedure TOnTerminateTestForm.PingMenuClick (Sender: TObject);

var
    sServer : String;

begin
    if (InputQuery ('Ping computer', 'Computer name:', sServer)) then
        if (sServer <> '') then
        begin
            PingMenu.Enabled := false;
            LogMemo.Lines.Add (Format ('Pinging %s',[sServer]));
            BackgroundPing (sServer, PingResult);
        end; { if }
end; { TOnTerminateTestForm.PingMenuClick }

procedure TOnTerminateTestForm.BackgroundPing (const sServer: String;
                                               const OnResult: TPingResultEvent);

var
    bResult : Boolean;

begin
    Parallel.Async (
        procedure
        begin
            bResult := Ping (sServer);
        end,

        Parallel.TaskConfig.OnTerminated(
            procedure (const task: IOmniTaskControl)
            begin
                // executed in main thread after the async has finished
                if Assigned (OnResult) then
                    OnResult (bResult);
            end
      )
    );
end; { TOnTerminateTestForm.BackgroundPing }

procedure TOnTerminateTestForm.PingResult (const bResult: Boolean);
begin
    PingMenu.Enabled := true;
    LogMemo.Lines.Add ('Ping result = ' + BoolToStr (bResult, true));
end; { TOnTerminateTestForm.PingResult }

end.

Code source: Get a function result asynchronously in Delphi using Omni Thread Library

Community
  • 1
  • 1
Olaf Hess
  • 1,453
  • 11
  • 18
  • Olaf, That was quite helpful. Thank you. Now, I am still not able to send messages from the Async portion out to an event monitor. Can you point me in the right direction to do that? (I am reviewing the OmniThread documentation, but I haven't seen what I'm missing.) – Various60 Feb 24 '17 at 01:43