Updating the UI from a Task

While we’re talking things Parallel, the other part of the same demo from last post showed spawning a single task that slept for awhile and then called ShowMessage.

procedure TFormThreading.Button1Click(Sender: TObject);
var
  aTask: ITask;
  begin
    // not a thread safe snippet
    aTask := TTask.Create(procedure
                          begin
                            sleep(3000); 
                            ShowMessage('Hello');
                          end);
    aTask.Start;
 end;

Note the comment.

The non-threadsafe part is the call to ShowMessage. Any update of the UI should be done from the main thread.

The quite reasonable question that followed after showing the code was “How would you make it threadsafe?”

The correct but not very useful answer is to do the call to ShowMessage from the main thread.

Multi-threaded Programming. Theory vs Practice

Multi-threaded Programming. Theory vs Practice

How? You have two main choices.

Both involve wrapping your UI code up in an anonymous method. If you pass that method to TThread.Synchronize, it will be executed on the main thread and your task will block until it is finished.

procedure TFormThreading.Button1Click(Sender: TObject);
var
  aTask: ITask;
begin
  aTask := TTask.Create(procedure
                        begin
                          sleep (5000);
                          TThread.Synchronize(nil,
                                              procedure
                                              begin
                                                ShowMessage ('Hello');
                                              end);
                        end);
  aTask.Start;
end;

If instead you pass it to TThread.Queue, your Task will continue on running (or in this case, finish, as it is all done), and at some point in the future your anonymous method will execute on the main thread.

procedure TFormThreading.Button1Click(Sender: TObject);
var
  aTask: ITask;
begin
  aTask := TTask.Create(procedure
                        begin
                          sleep (5000);
                          TThread.Queue(nil,
                                        procedure
                                        begin
                                          ShowMessage ('Hello');
                                        end);
                        end);
  aTask.Start;
end;

Which one you choose will be dependent on what you are doing. In general if I can avoid blocking I will, so if my situation allows it I use Queue for performance reasons.

If you’re looking for more details on the PPL, including some meatier examples, check out Danny Wind‘s CodeRage 9 session, and stay all the way to the end.

6 Comments

  • It seems to me that it is better to use a threadsafe string queue to send messages from threads to the UI. A timer on the UI would then check this queue every 30th of a second or so and post the messages it found. The idea of mixing UI code in with business logic just feels wrong to me.

  • That picture taught me more about parallel programming that any of your posts. 😉

    • Hey mate, not sure if that speaks to the awesomeness of that photo or the mediocrity of my posts, but I’ll go with the former 😉

  • I agree with your aim of not mixing the UI and business logic, but not using Synchronize or Queue because of it is possibly throwing the baby out with the bathwater.

    A solution I’ve used is for the UI layer to supply an anonymous method (or delegate) to the business layer, which could be invoked from within Synchronize/Queue. So you end up with the same result without the business layer knowing the intimate details of the UI.

    An alternative I’m looking at is to post a message from your business logic that can be subscribed to in the UI. I’m already using a message bus in many of my apps, so this fits nicely with that. http://www.malcolmgroves.com/blog/?p=1372

    If you really want to drop back to using a threadsafe queue and polling, then there is a threadsafe list and queue in System.Generics.Collections you could use.

  • […] One important thing to realise about the OnRequestManagerPassword event is that it is called in the context of a background thread (the background thread that is doing all the network activity). In this case, all I’m doing is referencing one of the event parameters, so I don’t have any issues around shared resources. However, let’s say you wanted to prompt the user to enter a password. That would involve interaction with the UI thread, and that would need to be synchronised. […]

Join the Discussion

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>