C# 在Windows服务中运行长时间运行的任务

C# 在Windows服务中运行长时间运行的任务,c#,windows-services,task-parallel-library,C#,Windows Services,Task Parallel Library,我编写了一个Windows服务项目,它承载一个长时间运行的消息泵任务,该任务将在服务期间运行。当服务启动时,它启动任务。当服务停止时,它将停止任务: void OnStart() { MessagePump.Start(); } void OnStop() { MessagePump.Stop(); } 其中MessagePump.Start执行Task.Factory.StartNew,MessagePump.Stop发出停止任务的信号并执行等待() 到目前为止还不错,但我

我编写了一个Windows服务项目,它承载一个长时间运行的消息泵任务,该任务将在服务期间运行。当服务启动时,它启动任务。当服务停止时,它将停止任务:

void OnStart()
{
    MessagePump.Start();
}

void OnStop()
{
    MessagePump.Stop();
}
其中MessagePump.Start执行Task.Factory.StartNew,MessagePump.Stop发出停止任务的信号并执行等待()

到目前为止还不错,但我想知道如何最好地处理故障。如果任务有一个未处理的异常,我希望服务停止,但是由于任务上通常没有任何东西在等待,我想它将什么也不做。我怎样才能优雅地处理这种情况

更新:

共识似乎是用“等待”或“继续”来表达。下面是我目前如何编写我的Start方法来使用它:

public async static void Start()
{
    this.state = MessagePumpState.Running;
    this.task = Task.Factory.StartNew(() => this.ProcessLoop(), TaskCreationOptions.LongRunning);
    try
    {
        await this.task;
    }
    catch
    {
        this.state = MessagePumpState.Faulted;
        throw;
    }
}
使MessagePump.Start()方法返回任务。然后

MessagePump.Start().ContinueWith(t => 
{
    // handle exception 
}, 
TaskContinuationOptions.OnlyOnFaulted);
更新: 我会做下一件事:

private MessagePump _messagePump;

async void OnStart()
{
    this._messagePump = new MessagePump();
    try
    {
        // make Start method return the task to be able to handle bubbling exception here
        await _messagePump.Start();
    }
    catch (Exception ex)
    {
        // log exception
        // abort service
    }
}

void OnStop()
{
    _messagePump.Stop();
}

public enum MessagePumpState
{
    Running,
    Faulted
}

public class MessagePump
{
    private CancellationTokenSource _cancallationTokenSrc;
    private MessagePumpState _state;
    public async Task Start()
    {
        if (_cancallationTokenSrc != null)
        {
            throw new InvalidOperationException("Task is already running!");
        }

        this._state = MessagePumpState.Running;
        _cancallationTokenSrc = new CancellationTokenSource();
        var task = Task.Factory.StartNew(() => this.ProcessLoop(_cancallationTokenSrc.Token), _cancallationTokenSrc.Token);
        try
        {
            await task;
        }
        catch
        {
            this._state = MessagePumpState.Faulted;
            throw;
        }
    }

    public void Stop()
    {
        if (_cancallationTokenSrc != null)
        {
            _cancallationTokenSrc.Cancel();
            _cancallationTokenSrc = null;
        }
    }

    public void ProcessLoop(CancellationToken token)
    {
        // check if task has been canceled
        while (!token.IsCancellationRequested)
        {
            Console.WriteLine(DateTime.Now);
            Thread.Sleep(1000);
        }
    }
}

您可以尝试以下方法:

void OnStart()
{
    MessagePump.StartAsync(); 
    MessagePump.ErrorEvent += OnError();
}
然后,您的StartAsync将类似于:

public async Task StartAsync()
{
     // your process
     // if error, send event to messagePump
}

如果您决定使用Task,那么最好使用Task.Run而不是Task.Factory.StartNew()

我建议您使用async/await和取消选项。当您有未处理的异常时,进行取消,您的程序就会知道错误。我可以在Windows服务中执行此操作吗?我的理解是,如果我在OnStart()中执行等待,那么在任务完成之前它不会退出?(可能是错误的)嗯。不等待MessagePump.Start()上的任务怎么样?看看这个博客,也许你会找到你想要的。如果我使用wait,代码执行可能会坐在那里等待任务结束,而不退出OnStart?@Barguast将此逻辑包装在一个异步方法中,使用wait,并放弃此异步方法返回的任务。这样,您就不必处理令人讨厌的连续剧,程序也会继续运行。@Barguast no,wait不会阻止您的调用线程。这和ContinueWith一样(几乎一样)。@sedovav-谢谢,这很有帮助。我已经用一个例子更新了我的第一篇文章,说明了我的Start方法现在的样子。它使用wait和一个异常处理程序(仅用于设置成员字段),并且似乎按照我预期的方式工作。这是一个“async void”,虽然看起来有点奇怪,但返回任务(并避免编译器警告)更有意义。如果要求不太多,你能看一下我是否做错了什么吗?@Barguast将代码包装在异步任务中,并放弃生成的任务。添加代码注释。Async void以您可能不希望(或不理解)的方式与同步上下文交互。谢谢。我在我的类中添加了一个“Faulted”事件,这似乎就是ServiceHost处理此事件的方式。你的最后一句话也很有趣——我认为Task.Run基本上与Task.Factory.StartNew相同,但减去了一些复杂的选项。我想我至少应该在我的任务上设置LongRunning。不确定Task.Run是否还有其他好处?@Barguast请看一下这张图片