OmniThreadLibrary forum
News: SMF - Just Installed!
 
*
Welcome, Guest. Please login or register. May 17, 2012, 05:56:37 PM


Login with username, password and session length


Pages: [1] 2   Go Down

Author Topic: Passing parameters to function which will be executed by Async  (Read 1018 times)

Unspoken

  • Newbie
  • *
  • Posts: 18
    • View Profile
    • Email
Passing parameters to function which will be executed by Async
« on: November 23, 2011, 01:18:33 PM »

Hello,

My question is similar to this one http://otl.17slon.com/forum/index.php/topic,302.0.html, but a little more elaborated:

I am in the process of evaluation the library and I have one concern - passing parameters.

Here is example which I've prepared and would like to discuss.

Code: [Select]
unit uMain;

interface

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

type
  TGlobalStructure = record
    X,Y,Z: Integer;
  end;

type
  TForm42 = class(TForm)
    btnOTL: TButton;
    btnStandardThread: TButton;
    procedure btnOTLClick(Sender: TObject);
    procedure btnStandardThreadClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    GlobalStructure: TGlobalStructure;
    procedure GetThreadResult(Sender: TObject);
    procedure OTLAddNumbers;
  end;

type
  TAddingThread = class(TThread)
  protected
    procedure Execute; override;
    public
      X,Y,Z: Integer;

  end;

function AddNumbers(const X, Y: Integer): Integer;

var
  Form42: TForm42;

implementation
uses
  OtlTaskControl, OtlParallel;


{$R *.dfm}

function AddNumbers(const X, Y: Integer): Integer;
begin
  Result := X + Y;
end;

procedure TForm42.btnOTLClick(Sender: TObject);
begin
  //First I must initialize global structure which is quite inconvenient
  //becasue I have to take care of that structure during executing of the thread.
  //I must watch out for it, not to modify it by accident, etc.
  GlobalStructure.X := 1;
  GlobalStructure.Y := 2;
  Parallel.Async(
    OTLAddNumbers,
    Parallel.TaskConfig.OnTerminated(
      procedure (const task: IOmniTaskControl)
      begin
        // executed in main thread
        ShowMessage(IntToStr(GlobalStructure.Z));
      end
    )
  );
end;

procedure TForm42.btnStandardThreadClick(Sender: TObject);
var
  AddingThread: TAddingThread;
begin
  //In the case of normal thread I do not have to worry about existance of
  //variables after running the thread because they are copied into the thread object.
  AddingThread := TAddingThread.Create(True);
  AddingThread.FreeOnTerminate := True;
  AddingThread.OnTerminate := GetThreadResult;
  AddingThread.X := 1;
  AddingThread.Y := 2;
  AddingThread.Resume;
end;

procedure TForm42.GetThreadResult(Sender: TObject);
begin
  ShowMessage(IntToStr(TAddingThread(Sender).Z));
end;

procedure TForm42.OTLAddNumbers;
begin
  //Passing variables via globals is ugly...
  GlobalStructure.Z := AddNumbers(GlobalStructure.X, GlobalStructure.Y);
end;

{ TAddingThread }

procedure TAddingThread.Execute;
begin
  Z := AddNumbers(X,Y);
end;

end.

In the above example I am comparing standard TThread approach to OTL approach. IMHO the problem with Async approach is passing parameters to underlying procedure via some global variables. I think this is quite unsafe and decreases readability of code.

I could solve that problem by writting a class which takes up the parameters and then executes Async, but that class would be a wrapper for Async which on its own is wrapper for TThread...

What design pattern would you suggest that in your opinion is best for solving this problem? How do you solve this problem within your programs?

Thanks for the answer,

regards.


Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #1 on: November 24, 2011, 02:34:32 AM »

I do agree, this is ugly. Luckily, this is not the intended way to use OtlParallel primitives. Better way is to use anonymous methods and allow them to capture input parameters automatically. Like this:

Code: [Select]
function AddNumbers(const X, Y: Integer): Integer;
begin
  Result := X + Y;
end;

procedure TForm64.AsyncAdd(a, b: integer);
begin
  Parallel.Async(
    procedure (const task: IOmniTask)
    var
      result: integer;
    begin
      result := AddNumbers(a, b);
      task.Invoke(
        procedure
        begin
          ShowMessage(IntToStr(result));
        end
      );
    end
  );
end;

procedure TForm64.btnOTLClick(Sender: TObject);
begin
  AsyncAdd(1, 2);
end;
Logged

Unspoken

  • Newbie
  • *
  • Posts: 18
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #2 on: November 26, 2011, 06:14:49 AM »

Thanks Primoz.

It is way better but requires a lot more of understanding;)

1. What is the difference between TOmniTask and TOmniTaskControl?
2. Does this construct requires two additional threads?

Code: [Select]
  Parallel.Async(
    procedure (const Task: IOmniTask)
    var
      Result: Integer;
    begin
      Result := AddNumbers(X, Y);
      Task.Invoke(
        procedure
        begin
          ShowMessage(IntToStr(Result));
        end
      );
    end
  );

One for executing procedure (const Task: IOmniTask) wich is not synchronized with main thread, and the other one is created here: Task.Invoke which is synchronized with main thread?

If I am mistaken, please correct me.

Thanks for your time.
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #3 on: November 26, 2011, 06:58:59 AM »

1. TOmniTask is an implementation of the IOmniTask interface, which is the interface that the worker thread can use to perform some tasks (for example, communicate with the main thread). TOmniTaskControl is an implementation of the IOmniTaskControl interface, which is the interface that the main thread can perform some tasks (for example, stop the worker thread).

2. No, there are two threads - main thread and worker thread. Invoke-d code is executing in the main thread.
Logged

Unspoken

  • Newbie
  • *
  • Posts: 18
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #4 on: November 27, 2011, 01:37:29 PM »

What is the differencte between:

Code: [Select]
      Task.Invoke(
        procedure
        begin
          ShowMessage(IntToStr(Result));
        end
      );

and

Code: [Select]
    Parallel.TaskConfig.OnTerminated(
      procedure (const task: IOmniTaskControl)
      begin
        // executed in main thread
        ShowMessage(IntToStr(GlobalStructure.Z));
      end
    )


Will the second code be runned in a blocking manner? Simmilar to Synchronize()? On your blog you wrote that with Task.Invoke we don't know when the code will be executed. Is it the same situation in case of Parallel.TaskConfig.OnTerminated?

Regards.

Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #5 on: November 27, 2011, 01:54:33 PM »

Both work exactly the same.

  • Task.Invoke / termination handler sends special 'terminated' message together with the code that has to be executed to a hidden window living in the main thread (this window is created by the OmniThreadLibrary).
  • Some time later (typically this happens very soon) main thread processes messages and during that also processes messages for this hidden window.
  • Window procedure of this hidden window then executes the code.
Logged

Unspoken

  • Newbie
  • *
  • Posts: 18
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #6 on: November 27, 2011, 02:11:40 PM »

Wow.

Thanks for sharing the "under the hood" deatails. :)

However, what if I am not creating a GUI application? For example a service application. OTL is including a Forms unit into the project or you are creating this hidden window by Windows API?
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #7 on: November 27, 2011, 02:36:19 PM »

I believe that this would not work for service applications at the moment.
Logged

Unspoken

  • Newbie
  • *
  • Posts: 18
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #8 on: November 27, 2011, 04:40:51 PM »

I believe that this would not work for service applications at the moment.

Whole OTL or just the Invoke?

I've read about some tricks allowed to create invisible windows for service applications.
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #9 on: November 27, 2011, 04:42:56 PM »

I meant OTL messaging back to the main form. On the other hand, it might just work. If you can set up small service application and test it, I would be very grateful.
Logged

Unspoken

  • Newbie
  • *
  • Posts: 18
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #10 on: November 27, 2011, 05:39:03 PM »

I surly will do it. But after some time when I gain more knowledge about OTL :)
Logged

Soft landing

  • Newbie
  • *
  • Posts: 7
    • View Profile
Re: Passing parameters to function which will be executed by Async
« Reply #11 on: December 13, 2011, 07:51:21 AM »

I do agree, this is ugly. Luckily, this is not the intended way to use OtlParallel primitives. Better way is to use anonymous methods and allow them to capture input parameters automatically. Like this:

Code: [Select]
procedure TForm64.AsyncAdd(a, b: integer);
begin
  Parallel.Async(
    procedure (const task: IOmniTask)
    var
      result: integer;
    begin
      result := AddNumbers(a, b);
      task.Invoke(
        procedure
        begin
          ShowMessage(IntToStr(result));
        end
      );
    end
  );
end;
This puzzles me. The way I understand it the procedure with the ShowMessage call in it is run in the main thread, and it is possible/likely that this will run at some time after the procedure run by parallel.async has exited. I would have thought there is no guarantee that the result local variable will still be valid when the ShowMessage call is made. Doesn't the ShowMessage call use a variable which is not in scope and may have been overwritten?
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #12 on: December 13, 2011, 07:58:47 AM »

Compiler makes a copy of the 'result' variable and uses this copy inside the anonymous method.
Logged

Soft landing

  • Newbie
  • *
  • Posts: 7
    • View Profile
Re: Passing parameters to function which will be executed by Async
« Reply #13 on: December 13, 2011, 09:31:54 AM »

That was a quick answer! impressive.

So if result were an object that had to be destroyed, how would you do it? I just tried it and the only way I got it working was like this:
Code: [Select]
procedure TForm3.btnTestClick(Sender: TObject);
begin
  parallel.Async(
    procedure (const Task : IOmniTask)
    var
      result : TStrings;
    begin
      result := TStringList.Create;
      BackgroundTask(result);

      Task.Invoke(
        procedure
        begin
          Memo1.Lines.AddStrings(result);
          result.Free;
        end);
    end);
end;
If I destroy the result object outside of the invoked task it is not valid (has already been destroyed) when I try to add the strings to the memo. If I do it as shown here, where do I put my "try ... finally" to make sure result is freed?
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Passing parameters to function which will be executed by Async
« Reply #14 on: December 13, 2011, 09:40:37 AM »

1) Yes, this is the correct approach.
2) You cannot use try..finally in this example; that's why interfaces are much more suitable for interthread communication than classes because they get destroyed automatically.
Logged
Pages: [1] 2   Go Up
 
 

Powered by MySQL Powered by PHP Powered by SMF 2.0.2 | SMF © 2006-2009, Simple Machines LLC

Valid XHTML 1.0! Valid CSS! Dilber MC Theme by HarzeM