Pop quiz. What’s wrong with this code?
Looks fine, but it doesn’t work. In most cases, running this code fragment would cause immediate access violation.
This is a common problem amongst new OTL users. Heck, even I have fallen into this trap!
The problem here is that CreateTask returns IOmniTaskControl interface, or task controller. This interface must be stored into some persistent location, or task controller would be destroyed immediately after Run is called (because the reference count would fall to 0).
A common solution is to just store the interface in some field.
FTaskControl := CreateTask(MyWorker).Run;
When you don’t need background worker anymore, you should terminate the task and free the task controller.
FTaskControl.Terminate; FTaskControl := nil;
This works for background workers with long life span – for example if there’s a background thread running all the time the program itself is running. But what if you are starting a short-term background task? In this case you should monitor it with TOmniEventMonitor and cleanup task controller reference in OnTerminate event handler.
FTaskControl := CreateTask(MyWorker).MonitorWith(eventMonitor).Run;
FTaskControl := nil;
As it turns out, event monitor keeps task controller interface stored in its own list, which will also keep the task controller alive. That’s why the following code also works.
Since OTL v1.04 you have another possibility – write a method to free the task controller and pass it to the OnTerminated.
FTaskControl := CreateTask(MyWorker).OnTerminated(FreeTaskControl).Run; procedure FreeTaskControl(const task: IOmniTaskControl); begin FTaskControl := nil; end;
If you’re using Delphi 2009 or 2010, you can put the cleanup code in anonymous method.
FTaskControl := CreateTask(MyWorker).OnTerminated( procedure(const task: IOmniTaskControl) begin FTaskControl := nil; end) .Run;
OnTerminated does its magic by hooking task controller into internal event monitor. Therefore, you can get real tricky and just write “null” OnTerminated.
CreateTask(MyWorker).OnTerminated(DoNothing).Run; procedure DoNothing(const task: IOmniTaskControl); begin end;
As that looks quite ugly, I’ve added method Unobserved just few days before version 1.04 was released. This method does essentially the same as the “null” OnTerminated approach, except that the code looks nicer and programmers intentions are more clearly expressed.