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; 1 2 3 4 5 6 7 8 9 10 11 12 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.

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; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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.