β Task tracking
Hi!
I am writing an app that relies on HostedService(s).
I've got a hosted services which has an HttpListener and whenever a new request comes in I use a Channel<T> to dispatch it to another HostedService that consumes this Channel.
This all works great!
Now, inside the
RequestQueueWorker
(the consumer of the Channel). I use Task.Run()
and add the Task proxy into a list.
So that in case of a graceful application shutdown I can use the StopAsync
method of the HostedService
to await Task.WhenAll(_runningTasks)
to ensure that those tasks are indeed completed.
However, ideally I'd want to clean up this list every once in a while and get rid of all tasks that are completed so that this list stays small.
Throughout the lifetime of the app there could be thousands if not more Task
's inside of this list.
Is the clean up even needed from the get go? Is it overkill? Should I even care optimizing this part considering it's only on shutdown anyway?
Thanks in advance!
I hope I provided enough information, if not feel free to ask for more details!
(Please tag me if you reply, so I get a notitication. Thanks π )7 Replies
@Jer maybe you can use
.ContinueWith(...)
method that accepts TaskContinuationOptions
enum for each task to remove itself on completion from your collection of tasks
@Jer you should avoid ever growing collections in any case. Cleaning only on shutdown is not enoughThanks! This would mean Iβd have to lock on the List though. Right? Otherwise when I iterate it on shutdown the collection might be modified by the continuation during iteration of Task.WhenAll()
(lock only just before calling Task.WhenAll()) to ensure exclusive access from the shutdown sequence)
@Jer maybe you can use
ConcurrentBag<T>
instead of List<T>
to avoid locking by yourself.
Another alternative is SynchronizedCollection<T>
if you need index access to the list. Maybe ConcurrentBag<T>
is enough for your use caseYeah that should be sufficient, although it might slow down the performance overall due it locking whenever any task finishes whereas with the manual lock itβs only on shutdown a single lock op until Task.WhenAll finishes
(assuming the ConcurrentBag<T> locks on add/remove)
@Jer also remember to propagate or request from DI the cancellationToken that fires when shutdown is requested in every logic the tasks do because cancellation should be cooperative. ASP .NET Core by default gives a couple of seconds to allow gracefull shutdown and after these seconds pass, the process exits anyway not waiting for your background work
Iβve increased the shutdown timeout
Yeah Iβm passing down the CT everywhere. I just want to do the most I can to process anything that was sent while the HttpListener was still processing even though the application was already gracefully being shut down
Thanks for your help! @David_F
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.