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

Why does Task.Run() in windows service OnStart() cause the service to refuse to stop?

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

我 运行 试图纠正启动服务时抛出的 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),我无法通过常规方法停止服务。

第一次尝试:

所有后续尝试:

编辑:还是不开心

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

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

当你调用_cancelTokenSource.Cancel()时,如果你还没有注册一个回调函数,所有发生的事情就是一个布尔值isCancellationRequested被设置为true,然后DoWork()方法负责看到这一点并自行停止执行。但是,这里有一个缺陷,您可能会说,如果调用 Cancel()DoWork() 任务中有一个耗时的循环 运行,则该循环将必须完成在它可以检查 isCancellationRequested 的值之前的迭代,这可能导致挂起。

解决方法是在 DoWork() 方法中插入取消回调函数,参见 here 然后将它们注册到令牌中,这样当您调用 Cancel() 方法时,后台的所有任务 运行 都已停止,无需等待它们。

希望对您有所帮助!