将任务合并到课堂中的开发模式

时间:2012-08-10 05:57:04

标签: c# task-parallel-library

我一直在玩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     
};

问题2:这是我需要采取的方法吗?

我知道有些人可能会建议将Start方法创建为此类之外的Task,并在调用代码中处理cancel \ exceptions,但是在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
        }
    }

关于向客户提出例外的说明

有一次,我确实调查了向客户端抛出异常(我知道这不是原始问题的内联,但我很好奇)并在那里处理\ log。一种方式(我看到其他几个)这样做是因为当一个异常发生时,这个类以Exception作为参数引发一个事件。客户需要确保订阅此事件以通知异常。

我不需要这个功能,而且它使事情变得复杂,而我只是在类本身中执行所有处理\记录异常。

良好的TPL选项文档

看一看 http://download.microsoft.com/download/B/C/F/BCFD4868-1354-45E3-B71B-B851CD78733D/TPLOptionsTour.pdf 它查看了运行任务时的不同“选项”,并且我想到了使用'OnlyOnFaulted'方法,这是异常处理的典型用法(参见第19页)。

欢迎任何进一步的评论。

2 个答案:

答案 0 :(得分:1)

第二种方法显然更好,因为它不需要Start方法等到“即发即忘”初始化完成。

请注意,如果有人在启动代码仍在运行时调用停止。这可能是竞争条件。你可能需要在这里进行同步。

您还可以在stop方法中跟踪同步HashSet<Task>WaitAll中正在运行的所有任务。这将确保Stop完成后仍然没有代码运行。

答案 1 :(得分:0)

我将回答我自己的问题。其他帖子虽然很有帮助,但对我提出解决方案并没有太大贡献