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

本文关键字:运行 长时间 任务 Windows 服务 | 更新日期: 2023-09-27 18:23:45

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

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

其中MessagePump.Start执行Task.Factory.StartNew,MessagePump.Stop发出停止任务的信号并执行Wait()。

到目前为止还不错,但我想知道如何最好地处理错误。如果任务有一个未处理的异常,我希望服务停止,但由于通常没有什么东西在等待任务,我想它会无所事事。我该如何优雅地处理这种情况?

更新:

共识似乎是使用"等待"或"继续"。以下是我目前如何编码我的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;
    }
}

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

让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
}

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