我正在尝试构建Windows服务,它需要某种并行性来汇集来自不同ftp源的文件。为了启动多个ftp下载,我正在寻找TPL库来轻松地进行foreach循环并使并行性变得非常容易。但是,当我搜索如何开始或停止我的服务时,我最好的资金是在OnStart()
方法中创建新线程,如此处所述https://stackoverflow.com/a/4865893/69433
关于TPL,请注意TPL比手动线程和手动停止线程更先进。
我没有找到任何描述如何在WindowsService中进行TPL循环的示例帖子?
我的代码:
protected override void OnStart(string[] args)
{
_thread = new Thread(WorkerThreadFunc);
_thread.Name = "My Worker Thread";
_thread.IsBackground = true;
_thread.Start();
}
在WorkerThreadFunc
里面做一些TPL
private void WorkerThreadFunc()
{
foreach (string path in paths)
{
string pathCopy = path;
var task = Task.Factory.StartNew(() =>
{
Boolean taskResult = ProcessPicture(pathCopy);
return taskResult;
});
task.ContinueWith(t => result &= t.Result);
tasks.Add(task);
}
}
或者我应该将TOTK也作为TASK启动我的WorkerThreadFunc?
答案 0 :(得分:21)
这里不仅告诉你如何开始你的工作人员,还要知道你如何停止它。
除了Task本身,TPL还通过使用CancellationToken
和CancellationTokenSource
对象为您提供了一种非常方便的任务取消方式。
要将此技术应用于Windows服务,您基本上需要执行以下操作:
private CancellationTokenSource tokenSource;
private Task backgroundTask;
protected override void OnStart(string[] args)
{
tokenSource = new CancellationTokenSource();
var cancellation = tokenSource.Token;
backgroundTask = Task.Factory.StartNew(() => WorkerThreadFunc(cancellation),
cancellation,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
}
注意:
TaskCreationOptions.LongRunning
提示任务调度程序,该任务可能需要一个单独的线程,并避免将其放在ThreadPool上(以便它不会阻止池中的其他项)CancellationToken
传递给WorkerThreadFunc,因为这是通知它停止工作的方式 - 请参阅下面的取消部分在OnStop方法中,您触发取消并等待任务完成:
protected override void OnStop()
{
bool finishedSuccessfully = false;
try
{
tokenSource.Cancel();
var timeout = TimeSpan.FromSeconds(3);
finishedSuccessfully = backgroundTask.Wait(timeout);
}
finally
{
if (finishedSuccessfully == false)
{
// Task didn't complete during reasonable amount of time
// Fix your cancellation handling in WorkerThreadFunc or ProcessPicture
}
}
}
通过调用tokenSource.Cancel();
,我们只需告知此CancellationToken
发出的每个tokenSource
都会被取消,并且接受此类令牌的每个方法(如您的WorkerThreadFunc)现在应该停止< / em>它的工作。
处理取消特定于实施,但一般规则是您的方法应该监控取消令牌状态,并且能够在合理的时间内停止其工作。这种方法要求您逻辑将您的工作分成更小的部分,这样您就不会做一些需要花费大量时间才能完成并且不会开始任何新工作的工作如果要求取消。
查看您的WorkerThreadFunc代码,您可以考虑在执行每个新的ProcessPicture
任务之前检查取消,例如:
private List<Task> tasks = new List<Task>();
private void WorkerThreadFunc(CancellationToken token)
{
foreach (string path in paths)
{
if (token.IsCancellationRequested)
{
// you may also want to pass a timeout value here to handle 'stuck' processing
Task.WaitAll(tasks.ToArray());
// no more new tasks
break;
}
string pathCopy = path;
var task = Task.Factory.StartNew(() =>
{
Boolean taskResult = ProcessPicture(pathCopy, token); // <-- consider a cancellation here
return taskResult;
}, token); // <<--- using overload with cancellation token
task.ContinueWith(t => result &= t.Result);
tasks.Add(task);
}
}
如果ProcessPicture
需要很长时间才能完成,您可能还需要在其中添加取消支持。与WorkerThreadFunc类似,您应该考虑ProcessPicture
实现。这里的关键想法是找到一个可以安全停止工作并从方法返回的地方。通过安全地我的意思是 - 没有让系统或数据处于中断状态。
除了在WorkerThreadFunc中监视IsCancellationRequested
之外,您还可以Register
在请求取消时执行的回调,以及其他一些事情,例如清理等:
token.Register(CancellationCallback);
和
private void CancellationCallback()
{
// wait for all tasks to finish
// cleanup
}