3

How does one pass some parameters from the main thread to the working thread (ie the following procedure)?

Parallel.Async(
  procedure (const task: IOmniTask)
  begin
    //How does one pass in 'myParam' from the main thread, so that the statement bellow will work?
    s := task.Param['myParam'].AsString;
  end
);

If you check the definition of IOmniTaskConfig (in OtlParallel.pas), there is a commented out property called Param, like the following:

//    property Param: TOmniValueContainer read GetParam;

So I guess the answer to my question is no, but I wish it's not!

Edwin Yip
  • 4,089
  • 4
  • 40
  • 86

2 Answers2

4

You are expected to use variable capture for this.

var
  MyParam: Integer;
....
MyParam := 42;
Parallel.Async(
  procedure(const task: IOmniTask)
  begin
    Foo(MyParam);
  end
);

In case you are not familiar with variable capture for anonymous methods, it's discussed in some detail in the documentation.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • The same question was asked on the OTL forum which is currently offline. Here's a link to a cached copy of it: http://webcache.googleusercontent.com/search?q=cache:OCINfLjuG5sJ:otl.17slon.com/forum/index.php%3Ftopic%3D306.0+&cd=3&hl=en&ct=clnk&gl=uk As you can see, Primoz recommends variable capture. – David Heffernan May 16 '13 at 16:15
  • Thanks for the link, David! I did try to get to the OTL forum before posting here. I think that'll work. And I'm wondering, when inside `Foo()`, `MyParam` is physically a copy made by the compiler, while logically it's the same as the originally declared one? – Edwin Yip May 16 '13 at 16:27
  • I guess it'll also be OK if we write to `MyParam` inside `Foo()`? – Edwin Yip May 16 '13 at 16:32
  • You can write to it if you want. You can have all sorts of fun with that. Make a second call to `Async` and have both anon methods writing to `MyParam` and engineer communication between your anon methods! – David Heffernan May 16 '13 at 16:40
  • @EdwinYip: It's not "a copy that's logically the same." It's the same variable, but it lives in the anonymous method's backing functor object, not in the procedure it's declared in. I wrote up an explanation of the details of what's going on a few years back: http://tech.turbu-rpg.com/13/whats-in-a-name-less-method – Mason Wheeler May 16 '13 at 17:14
  • Sorry, I didn't see that question. Yes, there is no copy, just one instance of the variable per invocation of the function in which the variable is declared. – David Heffernan May 16 '13 at 17:27
  • Thank you all David and Mason! – Edwin Yip May 17 '13 at 12:46
  • To be clear, I assume there is no **inter-thread** violation to access the same variable in multiple threads? In other words, I don't have to use things like `Mutex`? – Edwin Yip May 17 '13 at 12:56
  • You'd have to deal with threading synchronisation issues if two threads write to write to the same variable. It's just a variable. Whenever you have two threads sharing a variable, the issues are the same. But all that is far removed from the question that you asked. Did I not answer that question? – David Heffernan May 17 '13 at 13:03
  • @DavidHeffernan, thanks for the explanation. I didn't ask my question clearly - actually I was wanting to know if there is something like `SetParameter('Message', 'Hello')` which is **thread-safe**, but for `Parallel.Async`. It's my fault :P never mind, you also answered that questions, so thanks. – Edwin Yip May 18 '13 at 08:02
4

My Short Answer

Parallel.Async is intended to be used with an anonymous method, and doing so allows you to capture variables from the calling method (as already demonstrated by David Heffernan).

My Long Answer (since you specifically asked about the Param collection)

The task.Param from your snippet doesn't refer to the property in the IOmniTaskConfig interface. It refers to the Param property in the IOmniTaskControl interface. It is a legitimate property, but to my knowledge (which isn't very much, considering that I just started looking at OTL today and haven't used it yet), you would have to write your own ASync procedure in order to write to the property in any type of meaningful way.

Out of the box, the Param property is meant to be used with OTL's lower-level functionality. For example:

FHelloTask :=
   CreateTask(RunHello, 'Hello')
   .SetParameter('Delay', 1000)
   .SetParameter('Message', 'Hello')
   .OnMessage(HandleTaskMessage)
   .OnTerminated(HandleTaskTerminated)
   .Run;

That code will create a thread and run the 'RunHello' procedure, which can access Delay and Message via it's task parameter. I.e. msg := task.Param['Message'];

ASync is basically a wrapper around CreateTask that automatically adds it into a thread pool, throws in some IOmniTaskConfig support, and has some error handling stuff. All in all, it's a class method with like 20 lines of code. It probably wouldn't be a terribly difficult matter to make your own version of ASync that implements the SetParameter procedure. I'm personally too tired to meddle with it right now, though, and I'd rather finish learning about OTL before I start hacking it up. Also, it's pretty easy to just use an anonymous method to capture the variables.

Aaron
  • 189
  • 6
  • Thanks for the help. Actually I've been also using the lower-level approaches and I like that it gives me fine grain control, and on the other hand, `Parallel.Async` is very convenient in some cases, since you keep all the logics together *in a single place*, as opposed to have the logics scattered all around. – Edwin Yip May 17 '13 at 12:51
  • Not a problem. `Parallel.Async` does look pretty handy. Really, the whole framework looks really neat. I spent a lot of time yesterday reading about OTL and getting all fired up and excited to try to incorporate it into my next project. Afterward, I came to the realization that such a goal clashes with my goal to start getting comfortable with Firemonkey (so I can produce multi-platform applications if I need to). Makes me want to cry. – Aaron May 17 '13 at 13:59
  • my very recent finding is that `CreateTask()`, OnTerminated(), etc, all accept anonymous methods! So `CreateTask()` is almost as handy as `Parallel.Async()`, and is much flexible! – Edwin Yip Oct 06 '13 at 15:42