C# 将任务纳入课堂的开发模式
我一直在玩TPL及其相关任务 问题1:我需要一些反馈,以了解我在如何将任务融入课堂方面是否走上了正确的道路 该类有一个Start和Stop方法 在Start的实现中,我想创建一个fire-and-forget任务来进行处理。调用实例的Start方法的代码应该可以根据需要自由调用Stop(例如,调用代码是一个windows服务,因此在OnStop事件中,我可能希望在任何实例上调用Stop,即“我想立即关机,以便所有人都停止您正在做的事情!”) 我见过很多类似于下面的代码C# 将任务纳入课堂的开发模式,c#,task-parallel-library,C#,Task Parallel Library,我一直在玩TPL及其相关任务 问题1:我需要一些反馈,以了解我在如何将任务融入课堂方面是否走上了正确的道路 该类有一个Start和Stop方法 在Start的实现中,我想创建一个fire-and-forget任务来进行处理。调用实例的Start方法的代码应该可以根据需要自由调用Stop(例如,调用代码是一个windows服务,因此在OnStop事件中,我可能希望在任何实例上调用Stop,即“我想立即关机,以便所有人都停止您正在做的事情!”) 我见过很多类似于下面的代码 Task myTask =
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
};
关于向客户端抛出异常的说明
在某一点上,我确实调查过向客户端抛出异常(我知道这与最初的问题不一致,但我很好奇)并在那个里处理\log。实现这一点的一种方法(我还看到了其他几种方法)是,当发生异常时,让此类以异常作为参数引发事件。客户端需要确保其订阅此事件,以便收到异常通知
我不需要这个功能,它使事情变得复杂,相反,我只是在类本身中处理\记录异常
好的第三方物流选项文档
看看
它考察了运行任务时的所有不同“选项”,也是我想到使用“OnlyOnFaulted”方法的地方,这是异常处理的典型用法(参见第19页)
欢迎任何进一步的评论。第二种方法显然更好,因为它不需要
Start
方法等待“触发并忘记”初始化完成
但是,如果有人在启动代码仍在运行时调用stop,请小心。这可能是一种竞赛条件。您可能需要在这里同步
您还可以在stop方法中跟踪同步的
HashSet
和WaitAll
中所有正在运行的任务。这将确保停止完成后没有代码仍在运行。我将用我自己的答案回答我的问题。其他帖子虽然很有帮助,但对我提出解决方案并没有太大帮助其中有一些好的地方。与任务开始相关联的取消令牌将用于在Stop方法中取消任务,执行代码将检查是否已发出取消请求,因此我认为这将避免问题。我不打算在课堂上运行一个以上的任务,但这当然可能会改变。如果我更新公共数据,我会记住比赛条件。我会对别人说的话感兴趣。在这一点上,我将做一个较小的测试应用程序,看看事情是如何运作的。还可能需要考虑如何处理任务中异常生成的聚合异常。
//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
}
}