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