使用Tasks作为将结果计算与提交结果分开的方法

时间:2013-05-10 20:28:30

标签: c# asynchronous task-parallel-library

我们正在考虑将计算某些结果的工作与提交这些结果的工作分开的API模式是:

    interface IResults { }
    class Results : IResults { }

    Task<IResults> CalculateResultsAsync(CancellationToken ct)
    {
        return Task.Run<IResults>(() => new Results(), ct);
    }

    void CommitResults(IResults iresults)
    {
        Results results = (Results)iresults;
        // Commit the results
    }

这将允许客户端具有启动某些结果计算的UI,并知道计算何时准备好,然后在那时决定是否提交结果。这主要是为了帮助我们处理在计算过程中UI将允许用户取消操作的情况。我们希望确保:

  • 取消UI仅在操作仍可取消时显示(即,一旦我们进入CommitResults,就没有返回),因此一旦CalculateResultsAsync任务完成,我们将取消取消用户界面,只要用户没有取消,请继续并调用提交方法。
  • 我们不希望有一个案例(即竞争条件),用户点击取消并且结果仍然提交。
  • 除了将IResults传递给CommitResults之外,客户永远不会使用IResults

问题: 一般的问题是:这是一个好方法吗?具体做法是:

  1. 由于客户端从不检查{{1}},因此将它拆分为两种方法并不合适,因为它们只是将其交回给Commit方法。
  2. 这个问题有标准方法吗?

2 个答案:

答案 0 :(得分:3)

这是非常标准模式(如果不是理想模式),尤其是当您的Results对象是不可变的时。我们定期在Visual Studio代码库中的TPL使用代码中执行此操作。当您的异步/并行逻辑处理数据时,总是存在很多快乐,并且变异的废话与此不同。

如果您熟悉或听说过“Roslyn”项目,这是我们实际上鼓励人们使用的模式。这个想法是重构可以在后台异步处理并产生一个对象,就像你的结果一样,表示正在应用重构的结果。然后,在UI线程上,任何人都可以使用其中一个结果对象并应用它,然后更新所有文件以包含新文本。

我确实发现整个IResults / Results的事情有点奇怪 - 目前尚不清楚你是否使用它来隐藏自己的实现。如果空接口和强制转换错误,您可以考虑向IResult添加一个提交方法,结果对象实现该方法。由你决定。

答案 1 :(得分:0)

我不确定为什么你需要这种模式。对我来说,似乎如果你在开始提交之前检查CancellationToken,你将得到完全相同的结果,界面更简单。