【问题标题】:Is there a proper pattern for multiple ContinueWith methods多个 ContinueWith 方法是否有适当的模式
【发布时间】:2018-12-03 11:59:13
【问题描述】:

In the docs for TPL我找到了这一行:

从同一个先行词调用多个延续

但这没有进一步解释。我天真地假设您可以以类似模式匹配的方式链接 ContinueWiths,直到您找到正确的 TaskContinuationOptions

TaskThatReturnsString()
    .ContinueWith((s) => Console.Out.WriteLine(s.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
    .ContinueWith((f) => Console.Out.WriteLine(f.Exception.Message), TaskContinuationOptions.OnlyOnFaulted)
    .ContinueWith((f) => Console.Out.WriteLine("Cancelled"), TaskContinuationOptions.OnlyOnCanceled)
    .Wait();

但这并没有像我希望的那样工作,至少有两个原因。

  • 延续已正确链接,因此第二个 ContinueWith 从第一个获得结果,即作为新任务实现,基本上是 ContinueWith 任务本身。我意识到可以继续返回字符串,但这不会是一个丢失其他信息的新任务吗?
  • 由于没有满足第一个选项,任务就被取消了。这意味着永远不会满足第二组并且会丢失异常。

那么当他们说来自同一个先行词的多个延续时,他们在文档中是什么意思? 是否有合适的模式,还是我们只需将调用包装在 try catch 块中?


编辑

所以我想这就是我希望我能做的,注意这是一个简化的例子。

    public void ProccessAllTheThings()
    {
        var theThings = util.GetAllTheThings();
        var tasks = new List<Task>();
        foreach (var thing in theThings)
        {
            var task = util.Process(thing)
                        .ContinueWith((t) => Console.Out.WriteLine($"Finished processing {thing.ThingId} with result {t.Result}"), TaskContinuationOptions.OnlyOnRanToCompletion)
                        .ContinueWith((t) => Console.Out.WriteLine($"Error on processing {thing.ThingId} with error {t.Exception.Message}"), TaskContinuationOptions.OnlyOnFaulted);
            tasks.Add(task);
        }
        Task.WaitAll(tasks.ToArray());
    }

因为这是不可能的,所以我想我必须将每个任务调用包装在循环内的 try catch 中,这样我就不会停止进程,也不会在那里等待。我不确定正确的方法是什么。

有时解决方案只是盯着你的脸,这行不通?

    public void ProccessAllTheThings()
    {
        var theThings = util.GetAllTheThings();
        var tasks = new List<Task>();
        foreach (var thing in theThings)
        {
            var task = util.Process(thing)
                .ContinueWith((t) =>
                {
                    if (t.Status == TaskStatus.RanToCompletion)
                    {
                        Console.Out.WriteLine($"Finished processing {thing.ThingId} with result {t.Result}");
                    }
                    else
                    {
                        Console.Out.WriteLine($"Error on processing {thing.ThingId} - {t.Exception.Message}");
                    }
                });
            tasks.Add(task);
        }
        Task.WaitAll(tasks.ToArray());
    }

【问题讨论】:

  • 如果您想创建处理块的管道,请使用 TPL 数据流库。不要尝试自己构建它。
  • 使用 try/catch 等待任务和处理潜在异常会更容易
  • @PanagiotisKanavos 我不知道这个库。我会看一下,但我想这对于我设想的用例来说有点过头了。
  • 只需使用await 而不是手动添加延续。更好的错误处理语义是使用它的众多主要优势之一。
  • @Servy 如果它不是异步方法怎么办?公平地说,我当然可以把它变成 Async 方法。我想我有点愚蠢,如果我在循环中Await 会发生什么。循环是否继续迭代?如果是这样,那当然可以。

标签: c# task task-parallel-library continuewith


【解决方案1】:

您所做的是创建多个任务的顺序链。

您需要做的是将所有后续任务附加到第一个任务:

var firstTask = TaskThatReturnsString();
var t1 = firstTask.ContinueWith (…);
var t2 = firstTask.ContinueWith (…);
var t3 = firstTask.ContinueWith (…);

那么你需要等待所有的延续任务:

Task.WaitAll (t1, t2, t3);

【讨论】:

  • 这将导致我有 3 个任务,其中两个将抛出 TaskCanceledException。我希望只处理一个错误的任务,而不会导致进一步的异常。我猜 ContinueWith 并不是为此而设计的,您应该始终只包装一个 try catch / 或在适当的情况下返回一个 resultObject。
  • 好吧,显然你的问题不清楚。请改写它以详细说明您想要实现的目标。
  • 很公平,我只是暗示了我所追求的,而您在这里提出的建议肯定是某种形式的解决方案。但是你最终总是不得不封装一个 try catch,如果我的异常处理足够简单,我希望你可以不用它,
猜你喜欢
  • 2022-01-20
  • 1970-01-01
  • 2016-04-30
  • 2016-09-02
  • 2017-01-20
  • 2019-05-19
  • 2018-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多