In my last post on using Parallel Programming and the TParallel.For construct we learned about the new System.Threading unit and how to use TParallel to make looping faster. There are however times when you need to run multiple tasks that are not loops, but these can run in parallel.
TTask provides a class to create & manage interaction with instances of ITask. You can choose to WaitForAll or WaitForAny to finish before proceeding in code.
To give an example. Imagine you have two tasks. A and B.
If A takes 3 seconds and B takes 5 seconds how long does it take to get a result to a user?
- Sequentially (without TTask / ITask) = 8 seconds.
- Using TTask.WaitForAll = 5 seconds
- Using TTask.WaitForAny = 3 seconds
Depending on what your doing, the speed for return can be dramatically quicker. So lets look at a code example for WaitForAll.
procedure TFormThreading.MyButtonClick(Sender: TObject); var tasks: array of ITask; value: Integer; begin Setlength (tasks ,2); value := 0; tasks := TTask.Create (procedure () begin sleep (3000); // 3 seconds TInterlocked.Add (value, 3000); end); tasks.Start; tasks := TTask.Create (procedure () begin sleep (5000); // 5 seconds TInterlocked.Add (value, 5000); end); tasks.Start; TTask.WaitForAll(tasks); ShowMessage ('All done: ' + value.ToString); end;
The above example uses an Array of ITask to process a set of tasks. The result returned is 8000, but despite 8 seconds worth of sleep commands, the first 3 seconds run in parallel, leaving the second task to finish before returning 2 seconds later, which equates to a 3 second gain on sequentially running the two tasks; and all of this without having to create your own custom threads and managing them return. 🙂
While speeding up a task to run before returning is good, you can also use TTask to prevent the user interface locking up if you want to start something in the background. To do this, you can just run a single task and start it, for example
procedure TFormThreading.Button1Click(Sender: TObject); var aTask: ITask; begin // not a thread safe snippet aTask := TTask.Create (procedure () begin sleep (3000); // 3 seconds ShowMessage ('Hello'); end); aTask.Start; end;
This second example, if used, would allow the user to press the button multiple times resulting in multiple ShowMessage calls, however, used with care this is a powerful way to run task. This is also an example of asynchronous programming where you can start the Task, get on with other stuff, and then deal with the result as it returns.
As ITask is an interface, you can always create your own classes that use ITask if you so wish, providing great flexibility to the frame work.