I want to trigger a task to run on a background thread. I don’t want to wait on the tasks completion.
In .net 3.5 I would have done this:
ThreadPool.QueueUserWorkItem(d => { DoSomething(); });
In .net 4 the TPL is the suggested way. The common pattern I have seen recommended is:
Task.Factory.StartNew(() => { DoSomething(); });
However, the StartNew() method returns a Task object which implements IDisposable. This
seems to be overlooked by people who recommend this pattern. The MSDN documentation on the Task.Dispose() method says:
“Always call Dispose before you release your last reference to the Task.”
You can’t call dispose on a task until it is completed, so having the main thread wait and call dispose would defeat the point of doing on a background thread in the first place. There also doesn’t seem to be any completed/finished event that could be used for cleanup.
The MSDN page on the Task class doesn’t comment on this, and the book “Pro C#2010…” recommends the same pattern and makes no comment on task disposal.
I know if I just leave it the finalizer will catch it in the end, but is this going to come back and bite me when I’m doing lots of fire & forget tasks like this and the finalizer thread gets overwhelmed?
So my questions are:
- Is it acceptable to not call
Dispose()on theTaskclass in this case? And if so, why and are there risks/consequences? - Is there any documentation that discusses this?
- Or is there an appropriate way of disposing of the
Taskobject that I’ve missed? - Or is there another way of doing fire & forget tasks with the TPL?
There is a discussion about this in the MSDN forums.
Stephen Toub, a member of the Microsoft pfx team has this to say:
Update (Oct 2012)
Stephen Toub has posted a blog titled Do I need to dispose of Tasks? which gives some more detail, and explains the improvements in .Net 4.5.
In summary: You don’t need to dispose of
Taskobjects 99% of the time.There are two main reasons to dispose an object: to free up unmanaged resources in a timely, deterministic way, and to avoid the cost of running the object’s finalizer. Neither of these apply to
Taskmost of the time:Taskallocates the internal wait handle (the only unmanaged resource in theTaskobject) is when you explicitly use theIAsyncResult.AsyncWaitHandleof theTask, andTaskobject itself doesn’t have a finalizer; the handle is itself wrapped in an object with a finalizer, so unless it’s allocated, there’s no finalizer to run.