I have a .Net (v4.0) Windows Service Application that spins of a tpl task at the beginning that performs certain, long running activities and basically stays alive for the application's life time and as such is created with the TaskCreationOptions.LongRunning parameter value.
Whenever the service is stopped and the .OnStop() method is called, I .Cancel() the CancellationToken(Source) I handed over to the worker task when I created it and I want it's .OnlyOnCanceled(...) continuation task to run.
The thing is, the service/process shuts down without that continuation task to run through "entirely" - sometimes it quits rather quickly, sometimes it runs through completely, sometimes it doesn't.
This does make sense to me as that particular task is probably sitting on another thread than the main one and thereby has no way of "stalling"/blocking that main one to end.
As I do not have a SynchronizationContext in that windows service application I can't tell that continuation task to run there/on the main thread so I was wondering: how would I do that?
And more precisely, what's the best practice to handle an application shutdown with running tpl tasks?
You'll have to wait for your task to complete in the OnStop
method (or OnPause
and OnShutdown
).
You have about 20 seconds to do whatever you need in OnStop
. If you don't think your thread will complete in 20 seconds, you should call RequestAdditionalTime
. As soon as you return from OnStop
your service process can be terminated.
Using ContinueWith
will asynchronously invoke the delegate passed to it, regardless of whether you pass ExecuteSynchronously
or use a SynchronizationContext
. As soon as the ContinueWith
executes, assuming that's the last line of OnStop
, OnStop
return and returns control to the SCN (well, ServiceBase
, but it sets your service's state to STOPPED and returns control to the SCM to presumably terminate your process.
ExecuteSynchronously
means that the continuation runs synchronously with regard to the task it's continuing from. i.e. run on the same thread as the task (if possible). The task is likely not running on the thread that is calling ContinueWith
(otherwise it couldn't call ContinueWith
) so ExecuteSynchronusly
doesn't mean synchronously with regard to the call to ContinueWith
.
You need to do something like:
RequestAdditionalTime(TimeSpan.FromSeconds(30).Milliseconds);
cancellationToken.Cancel();
task.Wait();
in OnStop
, the Wait
means you won't exit from OnStop
until your task completes (or it takes longer than 30 seconds and your process gets terminated)
This happens so because the continuation task also runs asynchronously. In order for it to block you need to specify task continuation option:
...
t.ContinueWith(ct => {...}, TaskContinuationOptions.ExecuteSynchronously);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With