4

我一直在玩 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 页)。

欢迎任何进一步的评论。

4

2 回答 2

1

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

如果有人在开始代码仍在运行时调用停止,请小心。这可能是一个竞争条件。您可能需要在此处同步。

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

于 2012-08-10T10:21:53.883 回答
0

我会用我自己的答案来回答我的问题。其他帖子虽然有帮助,但对我提出解决方案没有太大帮助

于 2012-09-05T00:12:51.967 回答