I ran into this when trying to correct a TimeoutException
thrown when starting the service. Using:
public void OnStart()
{
_startTask = Task.Run(DoWork, _cancelTokenSource.Token);
}
private void DoWork(){ [listen for things and operate on them] }
public void OnStop()
{
_cancelTokenSource.Cancel();
_startTask.Wait();
}
I understand that implementing a simple timer will solve this, but that's not the point of my question. Why does the use of Task.Run(() => action, _tokenSource.Token)
resolve the TimeoutException
but causes the service to not respond to control messages?
After installing and starting the service (it's TopShelf BTW), I'm unable to stop the service by conventional methods.
Here is my attempt after following the provided example.
public void Start()
{
var token = _cancelTokenSource.Token;
Task.Factory.StartNew(() => Setup(token), token);
}
public void Stop()
{
_cancelTokenSource.Cancel();
//TearDown(); <-- original implementation of stop
}
private void Setup(CancellationToken token)
{
_mailman.SendServiceStatusNotification(_buildMode, "Started");
... create some needed objects
token.Register(TearDown);
InitializeInboxWatcherProcess(...);
}
private void TearDown()
{
_inboxWatcher.Terminate();
_mailman.SendServiceStatusNotification(_buildMode, "Stopped");
}
private void InitializeInboxWatcherProcess(...)
{
// pre create and initiate stuff
_inboxWatcher = new LocalFileSystemWatcherWrapper(...);
_inboxWatcher.Initiate();
}
public class LocalFileSystemWatcherWrapper : IFileSystemWatcherWrapper
{
// do FileSystemWatcher setup and control stuff
}
This is most likely because you either don't have a cancellation method, or there are subprocesses inside of DoWork()
that are still running when you call Cancel()
. As @Damien_The_Unbeliever said cancellation is a cooperative task.
When you call _cancelTokenSource.Cancel()
if you haven't registered a callback function all that happens is that a boolean value isCancellationRequested
is set to true, the DoWork()
method is then responsible for seeing this and stopping its execution on its own. There is a flaw here, though, as you can probably tell, that if you have a time consuming loop running in the DoWork()
task when Cancel()
is called, that loop will have to finish an iteration before it can check the value of isCancellationRequested
which can lead to hanging.
The way around this is to insert cancellation callback functions INTO the DoWork()
method, see here and then register them to the token, so that when you call the Cancel()
method, ALL of the tasks running in the background are stopped without having to wait for them.
Hope this helps!