使用Task <t>进行非实时任务</t>

时间:2015-02-04 05:35:40

标签: c# .net async-await task future

我有Operation类,比如...

public sealed class Operation
{
  public void DoSomething1(ArgType1 arg) {...}
  public void DoSomething2(ArgType2 arg) {...}
  ...
  public Task<bool> Execute() {...}
}

DoSomething方法包要完成,存储arg参数,然后Execute()方法将启动Task以原子方式一起完成这项工作。 DoSomething s的主要影响是副作用,但其中一些也有意义返回一个值,我的第一直觉是返回Task,如下所示......

  public Task<ResultType3> DoSomething3(ArgType3 arg) {...}

但问题是,Task不会“活着”。因为大多数任务都被认为是。 await Task的结果只有在Execute()被召唤开始工作之前才会毫无结果,因此我觉得这会让消费者感到困惑。好像DoSomething3()Execute()的返回值是非独立的任务。

我可以将Task<>包含在一个名为Result<>的新类型中,在内部它会保留Task<>,而Operation会保留在TaskCompletionSource<>Result 1}}并在Execute()的末尾设置await,以便客户在Task之后Execute返回Result后,可以观察{ {1}}。

public Result<T>
{ 
  internal Result(Task t) { _t = t; }
  public bool IsComplete { get { return _t.IsComplete; } }
  public T Result { get { return _t.Result; } }
  // Perhaps more methods delegating to the underlying Task
}

  public Result<ResultType4> DoSomething4(ArgType4 arg) {...}

包装Task的主要动机是与消费者沟通DoSomething3()的结果不是实时Task并且难以/不可能致电......

var result = await op.DoSomething4(x);

因为这可能会导致代码死锁,因为还没有人解雇Operation。请注意此Result<>类型与Nullable<>具有不同语义的相似性。

另一种方法是该方法返回一些不透明的对象,该对象将用作在Operation完成后从Execute()检索实际结果的键...

var token = op.DoSomething4(x);
...
var succeeded = await op.Execute();
if (! succeeded) return;
var result = op.RetrieveResult(token);

其中,检索结果的签名类似于......

public T RetrieveResult(Token<T> token) {...}

我想另一种选择是添加一个额外的参数作为回调,在实际结果可用时在Execute()结束时执行...

public void DoSomething5(ArcType5 arg, Func<ResultType5,Task> callback) {...}

因此,你可以看到我有几个不同的选择,没有强烈的直觉,哪个是最合适的。不幸的是,这可能主要只是一个品味问题,但我会很感激对不同方法的反馈。

1 个答案:

答案 0 :(得分:2)

我找不到一个理由让你有不同的方法只设置值(而不是那个问题的属性)和一个运行一切的方法。

但是如果你想保留这个设计,你可以做一些非常类似于TPL Dataflow的块。拥有Completion任务属性,该属性仅在Execute完成且DoSomething3无效时才会完成。这使用户可以理解可以等待整个操作(包括Execute),而不仅仅是DoSomething3

public sealed class Operation
{
    private TaskCompletionSource<bool> _tcs
    public Task Completion {get { return _tcs.Task;} }
    public void DoSomething3(ArgType2 arg) {...}
    ...
    public Task<bool> Execute() 
    {
        // ...
        _tcs.SetResult(false);
    }
}

用法:

operation.DoSomething3(arg);
await operation.Completion;