Windows 服务内的 TPL

TPL inside Windows Service

我需要在并行编写的 Windows 服务中执行一些任务。我正在使用 VS2013、.NET 4.5,这个帖子 Basic design pattern for using TPL inside windows service for C# 表明 TPL 是可行的方法。

下面是我的实现。我想知道是否有人可以告诉我我是否做对了!

public partial class FtpLink : ServiceBase
{
    private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
    private readonly ManualResetEvent _runCompleteEvent = new ManualResetEvent(false);

    public FtpLink()
    {
        InitializeComponent();

        // Load configuration
        WebEnvironment.Instance.Initialise();
    }

    protected override void OnStart(string[] args)
    {
        Trace.TraceInformation("DatabaseToFtp is running");

        try
        {
            RunAsync(_cancellationTokenSource.Token).Wait();
        }
        finally
        {
            _runCompleteEvent.Set();
        }
    }

    protected override void OnStop()
    {
        Trace.TraceInformation("DatabaseToFtp is stopping");

        _cancellationTokenSource.Cancel();
        _runCompleteEvent.WaitOne();

        Trace.TraceInformation("DatabaseToFtp has stopped");
    }

    private async Task RunAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            Trace.TraceInformation("Working");

            // Do the actual work
            var tasks = new List<Task>
            {
                Task.Factory.StartNew(() => new Processor().ProcessMessageFiles(), cancellationToken),
                Task.Factory.StartNew(() => new Processor().ProcessFirmware(), cancellationToken)
            };

            Task.WaitAll(tasks.ToArray(), cancellationToken);

            // Delay the loop for a certain time
            await Task.Delay(WebEnvironment.Instance.DatabasePollInterval, cancellationToken);
        }
    }
}

有几件事我会做不同的事情:

  1. OnStart 应该及时执行。通常的做法是将工作推迟到负责执行实际工作的后台线程。你实际上是在这样做 但是 通过调用 Task.Wait 阻塞了那个线程,这使得卸载到后台线程变得无用,因为执行再次变得同步。
  2. 您正在使用 sync over async anti-pattern,应该尽量避免这种情况。让调用方法并行调用工作。
  3. 我认为您可能正在以相反的方式使用 ManualResetEvent。您将 RunAsync 方法包装在 try-finally 块中,但您只是从 OnStop 调用 WaitOne。我不太确定你是否需要锁在这里,似乎(从你当前的代码来看)这段代码不是被并行调用的。相反,您可以将 RunAsync 返回的 Task 存储在一个字段中并等待它完成。
  4. 您正在使用阻塞版本,WaitAll。相反,您可以使用异步版本 Task.WhenAll,它可以异步等待。