Task Handling on Application Shutdown

北战南征 提交于 2019-12-05 04:37:29

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);

Specifying TaskContinuationOptions.ExecuteSynchronously on the continuation does not influence the antecedent. If you wait on the antecedent, then exit the service, your continuation might still not run!

The only way to do this is to wait for the continuation task to complete before returning from OnStop.

IMHO, this is a bad fit for use of TPL. Tasks aren't really meant for this kind of 'run forever' logic.

IME, it's much simpler/easier to start a new thread for this dedicated work. Since the default is Background = false, the CLR will automatically wait for it to complete before exiting (service rules still apply).

Add a private bool stopping; and then your thread method just needs to do while (stopping == false) { DoStuff(); } DoStoppingStuff();

Your OnStop then just sets stopping = true and your thread will exit the while loop and do your stopping code :)

IME you don't need to add volatile, but you certainly could

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!