我一直在玩 TPL 及其相关的任务。
问题 1:我需要一些反馈,就我如何尝试将任务整合到课程中而言,我是否走在正确的道路上。
该类有一个 Start 和 Stop 方法。
在 Start 的执行中,我想创建一个即发即弃的 Task 来进行处理。然后,调用实例的 Start 方法的代码应该可以根据需要自由调用 Stop(例如,调用代码是 Windows 服务,因此在 OnStop 事件中我可能想在任何实例上调用 Stop 即“我现在想关闭,所以每个人停止你正在做的事情!”)。
我见过很多类似于以下的代码
Task myTask = Task.Factory.StartNew(GoOffAndDoSomething, [associated cancellation token]);
try{
myTask.Wait();
}catch (AggregateException ae){
//Process aggregate exceptions as required
}
...但我不想等待,因为这阻塞了我的调用线程,我不能调用 Stop 方法(或做任何其他事情)等。
所以我想我必须做这样的事情......
Task myTask = Task.Factory.StartNew(GoOffAndDoSomething, [associated cancellation token]);
//Use non-blocking ContinueWith
myTask.ContinueWith(HandleBadStuffThatHappened, TaskContinuationOptions.NotOnRanToCompletion);
//The method to handle exceptions etc
Action<Task> HandleBadStuffThatHappened = (antecendent) =>
{
// "Observe" your antecedent's exception so as to avoid an exception
// being thrown on the finalizer thread
var badStuffHappened = antecendent.Exception;
//Handle\rethrow exception etc
};
问题 2:这是我需要采取的方法吗?
我知道有些人可能会建议将 Start 方法创建为此类之外的 Task 并在调用代码中处理取消\异常,但是在 Windows 服务中创建的类可能有很多实例,所以我只想创建 Task,并处理任何例外,在类本身中完成。
编辑:即将在这里回答我自己的问题,但我会将它留给评论一段时间,以防这些额外的信息使我的意图更清晰,并为其他人提供添加的机会
所以...我认为我走的是正确的道路。正如我的一个评论中所说,我已经做了一个小应用程序来玩基于我所做的进一步研究的方法。我的课程中的相关方法显示在这里。这和它的评论应该向您展示我目前提出的方法。我想我基本上在那里。
//Called by external client to get things rolling
public void Start()
{
//Could use Status property of Task but it is simpler to just use a class property than deal
//with all the different stages a Task can be in.
if (!IsRunning)
{
IsRunning = true; //set it first in case there are any delays\issues with starting up
_tokenSource = new CancellationTokenSource();
_processing = Task.Factory.StartNew(process, _tokenSource.Token);
//Use the OnlyOnFaulted task continuation option. This is different to
//my .NotOnRanToCompletion suggestion previously
_processing.ContinueWith(antecedent => HandleException(antecedent.Exception),
TaskContinuationOptions.OnlyOnFaulted);
//There's no 'Task.Wait' here, I just want to fire and forget this Task.
}
}
//Look at the call to ContinueWith in Start method. This is what I want to do if the Task goes to
//a Faulted state, ie an exception occurred.
private void HandleException(AggregateException ae)
{
IsRunning = false;
//Log or handle errors errors as required.
//ae.Flatten().InnerException will give the exception that caused the failure.
//Finally Dispose Task here. Typically I retry code a specified number of times
//(retry code not shown) before finally throwing the exception, and typically I don't do any
//explicit handling other than to Log\Alert the issue. So at this poin the Task is 'beyond saving'
//so get rid of it.
_processing.Dispose();
}
//Stop method which can be called by external client.
public void Stop()
{
//Use the cancellation token created in Start() to cancel the Task
_tokenSource.Cancel();
IsRunning = false; //set flag last in case something occurs during cancellation process
}
//What I wired up my Task to do
private void process()
{
while (!_tokenSource.IsCancellationRequested)
{
//So assuming normal UN-exceptional operation of your code then just
//do stuff here until Stop method called by client
}
}
关于向客户端抛出异常的说明
有一次,我确实调查了向客户端抛出异常(我知道这与原始问题不相符,但我很好奇)并在那里处理\记录。一种方法(我见过其他几种方法)是在发生异常时让此类引发以异常作为参数的事件。客户端需要确保它订阅此事件以收到异常通知。
我不需要这个功能,它使事情变得复杂,相反,我只是在类本身中对异常进行所有处理\记录。
良好的 TPL 选项文件
看看 http://download.microsoft.com/download/B/C/F/BCFD4868-1354-45E3-B71B-B851CD78733D/TPLOptionsTour.pdf 它会查看您在运行任务和是我想到使用“OnlyOnFaulted”方法的地方,这是异常处理的典型用途(参见第 19 页)。
欢迎任何进一步的评论。