为什么Windows服务OnStart()中的Task.Run()导致服务拒绝停止?

时间:2019-02-27 17:36:34

标签: c# windows-services task-parallel-library topshelf

我不确定为什么该服务拒绝停止。

在尝试更正启动服务时抛出的TimeoutException时遇到了这个问题。使用:

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

我知道实现一个简单的计时器可以解决此问题,但这不是我的问题。为什么使用Task.Run(() => action, _tokenSource.Token)可以解决TimeoutException但导致服务不响应控制消息的问题?

已观察到的问题

安装并启动该服务(它是TopShelf BTW)之后,我无法通过常规方法停止该服务。

第一次尝试:

enter image description here

所有后续尝试:

enter image description here

编辑:仍然没有乐趣

这是我遵循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
    }

1 个答案:

答案 0 :(得分:1)

这很可能是因为您没有取消方法,或者DoWork()内有子流程在您调用Cancel()时仍在运行。正如@Damien_The_Unbeliever所说,取消是一项合作任务。

如果您未注册回调函数而调用_cancelTokenSource.Cancel()时,发生的一切是将布尔值isCancellationRequested设置为true,则DoWork()方法负责查看并自行停止执行。但是,您可能会说,这里有一个缺陷,如果在调用DoWork()时在Cancel()任务中运行了一个耗时的循环,则该循环必须先完成一次迭代可以检查isCancellationRequested的值,这可能导致挂起。

解决方法是将取消回调函数插入DoWork()方法中,请参见here,然后将其注册到令牌中,以便在调用Cancel()方法时,ALL在后台运行的任务中的一部分无需等待即可停止。

希望这会有所帮助!