C# 将任务纳入课堂的开发模式

C# 将任务纳入课堂的开发模式,c#,task-parallel-library,C#,Task Parallel Library,我一直在玩TPL及其相关任务 问题1:我需要一些反馈,以了解我在如何将任务融入课堂方面是否走上了正确的道路 该类有一个Start和Stop方法 在Start的实现中,我想创建一个fire-and-forget任务来进行处理。调用实例的Start方法的代码应该可以根据需要自由调用Stop(例如,调用代码是一个windows服务,因此在OnStop事件中,我可能希望在任何实例上调用Stop,即“我想立即关机,以便所有人都停止您正在做的事情!”) 我见过很多类似于下面的代码 Task myTask =

我一直在玩TPL及其相关任务

问题1:我需要一些反馈,以了解我在如何将任务融入课堂方面是否走上了正确的道路

该类有一个Start和Stop方法

在Start的实现中,我想创建一个fire-and-forget任务来进行处理。调用实例的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     
};
关于向客户端抛出异常的说明

在某一点上,我确实调查过向客户端抛出异常(我知道这与最初的问题不一致,但我很好奇)并在那个里处理\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
        }
    }