用TPL编写Windows服务,要求是 Windows服务应每隔几分钟或几秒钟运行,并行执行4个任务。我已经实现了以下代码,但有一个疑问,如
Task[] taskRunners = new Task[Utility.conTaskRunners];
Log("Stating Initiate Tasks::" + DateTime.Now);
Log("Calling InitiateTaskRunner Tasks::" + DateTime.Now);
taskRunners[0] = Task.Factory.StartNew(() => InitiateTaskRunner());
taskRunners[1] = Task.Factory.StartNew(() => ScanTaskRunner());
taskRunners[2] = Task.Factory.StartNew(() => ConvertTaskRunner());
taskRunners[3] = Task.Factory.StartNew(() => ConvertedScanRunner());
Task.WaitAll(taskRunners);
Thread.Sleep(12000);
InitiateTasks();
InitiateTaskRunner如下所示
public void InitiateTaskRunner()
{
Log("Begening InitiateTaskRunner ::" + DateTime.Now);
string[] subDirs = Directory.GetDirectories(Utility.inputPath);
if (subDirs.Length > 0) //files eixsts go and process it
{
Parallel.ForEach(subDirs, dir =>
{
InitiateTaskWorker(dir);
});
}
//BEFORE I SET signal SHOULD this wait as above is PARALLEL.FOREACH?
_waitOnOne.Set(); //set signal for successor after finish
Log("Ending InitiateTaskRunner ::" + DateTime.Now);
}
同样地,其他3个TaskRunners将会是;基本上每个任务运行器将为每个子目录调用TaskWorker并异步处理文件并且eatch TASK Runner和每个任务工作者将是独立且并发的。在TaskWorker完成文件工作后,很快就会将一个taskWorker中的文件移动到另一个任务工作者位置,下一个任务工作人员将会拾取但不会触发一个任务工作者到另一个任务工作者。
这种方法的问题在于,当第一个任务运行器仍在处理并推送到taskrunner#2位置等时,有时2,3和4个任务运行器可能会退出而不做任何事情。并且将在下一次迭代中处理来自任务运行器位置2,3,4的文件。
有没有更好的设计?任何在TPL中退出异常处理的帮助都会很棒。
public void ScanTaskRunner()
{
Log("Begening ScanTaskRunner ::" + DateTime.Now);
string[] subDirs = Directory.GetDirectories(Utility.scanPath);
if (subDirs.Length > 0) //files exists so proceed
{
Parallel.ForEach(subDirs, dir =>
{
ScanTaskWorker(dir, false);
});
}
else //no files hence wait for prcedent TASK to complete
{
_waitOnOne.WaitOne();
Parallel.ForEach(Directory.GetDirectories(Utility.scanPath), dir =>
{
ScanTaskWorker(dir, false);
});
}
_waitOnTwo.Set(); //Go successor if waiting for this
Log("Ending ScanTaskRunner ::" + DateTime.Now);
}
public void ConvertTaskRunner()
{
Log("Begening ConvertTasktRunner ::" + DateTime.Now);
string[] subDirs = Directory.GetDirectories(Utility.convertPath);
if (subDirs.Length > 0) //files exists so process as usual
{
Parallel.ForEach(subDirs, dir =>
{
ConvertTaskWorker(dir);
});
}
else //no files hence wait for precedent TASK to complete
{
_waitOnTwo.WaitOne();
Parallel.ForEach(Directory.GetDirectories(Utility.scanPath), dir =>
{
ScanTaskWorker(dir, false);
});
}
_waitOnThree.Set(); //Successor is good to go if waiting
Log("Ending ConvertTasktRunner ::" + DateTime.Now);
}
public void ConvertedScanRunner()
{
Log("Begening ConvertedScanRunner ::" + DateTime.Now);
string[] subDirs = Directory.GetDirectories(Utility.convrtedScanPath);
if (subDirs.Length > 0) //files avaiable process as usual
{
Parallel.ForEach(subDirs, dir =>
{
ConvertedScanTaskWorker(dir);
});
}
else //no files wait until precendent TASK completes
{
_waitOnThree.WaitOne();
Parallel.ForEach(Directory.GetDirectories(Utility.scanPath), dir =>
{
ScanTaskWorker(dir, false);
});
}
Log("Ending ConvertedScanRunner ::" + DateTime.Now);
}
答案 0 :(得分:0)
请参阅Best way to do a task looping in Windows Service了解启动任务并继续完成任务的好方法。
就异常处理而言,您可能希望等待所有已运行的任务并捕获并处理{% load staticfiles %}
<script src="{% static 'app/eldarion/js/eldarion-ajax.min.js %}"></script>
{% load phileo_tags %}
{% phileo_widget user post %}
。任何抛出异常的任务都会AggregateException
为真。在每项任务中使用IsFaulted
与其他人合作并参与取消。合作的那些任务将ThrowIfCancellationRequested
属性设为真。
IsCanceled
至于保持任务同步以及知道何时处理文件或其他阶段已完成的问题,您可以使用EventWaitHandle(尽管有多种方法可以执行此操作)。等待句柄可能比其他一些替代品(如自旋锁)慢一点,因为它进入内核,但是等待文件处理它可能是一个好主意使用它。
此处的代码显示了PhaseTwo的外观。 PhaseOne看起来基本相同,但您正在为一个不同的处理委托等使用等待句柄。
public void Run(CancellationTokenSource cts)
{
_cts = cts;
Task[] tasks = new Task[4];
try
{
tasks[0] = Task.Factory.StartNew(() => PhaseOne(), cts.Token);
tasks[1] = Task.Factory.StartNew(() => PhaseTwo(), cts.Token);
tasks[2] = Task.Factory.StartNew(() => PhaseThree(), cts.Token);
tasks[3] = Task.Factory.StartNew(() => PhaseFour(), cts.Token);
Task.WaitAll(tasks);
}
catch (AggregateException ex)
{
// Handle exceptions
foreach(Exception inner in ex.InnerExceptions)
{
// do something
}
}
if (tasks.Any(t => t.IsFaulted))
{
// do something
}
if (tasks.Any(t => t.IsCanceled))
{
// do something
}
}