Sunday, April 17, 2011

Simple background tasks with OtlParallel

While there’s a simple way to execute one-shot background threads with the low-level OTL API (by calling CreateTask), there is no such high-level function in OtlParallel. At least, there wasn’t one until release 899 was committed to the SVN. (Incidentally, that happened 11 days ago but I was silent about it because I was working on another feature – TaskConfig – which will be completed “really soon now”.)
In short, Parallel.Async accepts an anonymous method, normal method or procedure (all of them must be without parameters) and executes it in a background thread. That’s all.


  Parallel.Async(
    procedure
    begin
      MessageBeep($FFFFFFFF);
    end,
For slightly more complicated scenarios, you can also provide a second parameter, which is an anonymous method, normal method or procedure (again without any parameters) that will execute after the background task is completed. This second part of code executes in the context of the main thread so you can interact with the VCL from it.
A new demo 46_Async demonstrates this.
procedure TfrmDemoParallelAsync.btnAsyncClick(Sender: TObject);
begin
  btnAsync.Enabled := false;

  Parallel.Async(
    procedure
    begin
      // executed in background thread
      Sleep(500);
      MessageBeep($FFFFFFFF);
    end,

    procedure
    begin
      // executed in main thread
      btnAsync.Enabled := true;
    end
  );
end;
Clicking the button disables it and executes background task. Method btnAsyncClick then immediately exits and your app can proceed executing other code.
After half a second sleep, MessageBeep is executed in a background thread. Background task then terminates. At the end, termination code that re-enables button btnAsync is executed in the main thread.
Simple, but effective.

7 comments:

  1. The way the code snippet is written it returns after half a second, instead of 5.
    I like this a lot. A no fuss method for running background SQL queries, long calculations, waiting for network transfers and the list goes on and on:)

    Best regards,
    Ajasja

    ReplyDelete
  2. Half a second, of course! Fixed the text, thanks!

    ReplyDelete
  3. Nice to see this, Primoz.
    This piece of code is very similar (in the intent) to a very useful class in the Android SDK:
    http://developer.android.com/reference/android/os/AsyncTask.html

    Thanks for your work.

    ReplyDelete
  4. Not sure why I can't pass the second parameter - all I get is "There is no overloaded version of Async that can be called with these arguments", I even tried the snippet above..

    ReplyDelete
  5. @Imagine, the syntax has changed. Read more about it here: http://www.thedelphigeek.com/2011/07/life-after-21-async-redux.html

    ReplyDelete
  6. Gabr,

    Been using this particular construct. Great stuff - except that if the main window is being destroyed when calling, for example, the OnFinish handler may try to access an invalid object. How can I fix this? (just asking if the control is null doesn't work because it may catch right in the middle of calling a destructor, it seems)

    ReplyDelete
    Replies
    1. I would forbid form destruction while Async is in progress. You can do that by handling the OnCloseQuery event.

      Delete