可以从TPL任务派生从方法返回更多细节吗?

我的原始方法如下:

string DoSomeWork(); 

方法DoSomeWork在其他线程上启动一些工作并返回执行ID(只是随机字符串)。 稍后我可以通过给定的执行ID查询结果。 重点是在作业完成之前使执行ID可用。

现在我想更改签名以返回Task,因此用户可以等待。

 Task DoSomeWork(); 

同时我仍然需要返回执行ID(例如用于跟踪),我看到几个选项。 首先,如果out参数,第二个是返回带有执行ID和任务的元组(在C#中,这看起来不是最佳选项),第三个是我实际想要问的。

如果我将创建将派生自Task类的类,该怎么办:

 public class ExtendedTask : Task { public string ExecutionID {get; set;} } 

这看起来不错吗? 或者最好决定其他选择?

PS在BCL中有一些派生自Task类。

更新 ,似乎我无法定义这个明确的enouthg。 但是我需要在作业完成之前访问ExecutionID,因此我不能使用Task.Result

我不会亲自扩展 Task ,我会把它组成 。 这样您就不必担心任何只返回Task API – 您可以将任务包装起来。 你可以拥有一个暴露底层任务的属性,而对于C#5异步目的,你可以在你自己的类型上实现awaiter模式 – 但我觉得创建你自己的派生类型可能弊大于利。 尽管如此,这主要是一种直觉。

另一种选择是以相反的方式工作:将您的额外状态存储在Task.AsyncState属性中; 毕竟,这就是它的用途。 这样,您可以轻松地传递任务,而不会丢失它在逻辑上属于的执行上下文。

我建议使用Task ,因为它允许您在任务的结果中“嵌入”其他信息。

例如,在您的情况下,有类似的东西可能是有意义的:

 class ExecutionResult { public int ExecutionID { get; set; } public string Result { get; set; } // ... } public Task DoSomeWork() { return Task.Factory.StartNew( () => { // Replace with real work, etc... return new ExecutionResult { ExecutionID = 0, Result = "Foo" }; }); } 

编辑以回应评论:

如果你需要“之前”任务完成的数据,并且为了其他目的而试图访问它,我建议创建一个包含Task和其他数据的类,并返回它,即:

 class ExecutionResult { public int ExecutionID { get; private set; } public Task Result { get; private set; } // ... Add constructor, etc... } public ExecutionResult DoSomeWork() { var task = Task.Factory.StartNew( () => { // Replace with real work, etc... return "Foo"; }); return new ExecutionResult(1, task); // Make the result from the int + Task } 

这仍然可以让您访问有关您的流程和Task / Task

如果您决定从TaskTaskinheritance,您可能会遇到这样的挫折: 必须在您的任务时指定为任务提供实际工作的ActionFunc委托。派生对象是构造的,以后不能更改。 即使基类构造函数没有Start()新创建的任务也是如此,事实上它可能直到很久以后才开始,如果有的话。

这使得在必须在其最终工作的完整细节可用之前创建实例的情况下,难以使用Task -derived类。

一个示例可能是众所周知的Task节点的无定形网络,这些节点在共享目标上工作,以便它们以临时方式访问彼此的Result属性。 保证在网络中的任意节点上都可以使用Wait()的最简单方法是在启动任何节点之前预先构造所有节点。 这巧妙地避免了尝试分析工作图依赖性的问题,并允许运行时因素确定何时,是否以及以什么顺序要求Result值。

这里的问题是,对于某些节点,您可能无法提供在构造时定义工作的function。 如果创建必要的lambda函数需要从网络中的其他任务中关闭Result值,那么提供我们想要的ResultTask可能尚未构建。 即使它恰好在预构建阶段就已经构建过了,你也不能在它上面调用Start() ,因为它可能会依赖于其他没有的节点。 请记住,预构建网络的重点是避免像这样的复杂性。

好像这还不够,还有其他原因,不得不使用lambda函数来提供所需的function。 因为它作为参数传递给构造函数,所以函数无法访问最终任务实例的this指针,这会产生丑陋的代码,特别是考虑到lambda必然是在 – 范围内定义 – 并且可能无意中关闭 – – 一些不相关的指针。

我可以继续,但最重要的是,在派生类中定义扩展function时,您不应该忍受运行时闭包膨胀和其他麻烦。 这不是错过了多态性的全部意义吗? 以正常方式定义Task -derived类的工作委托更为优雅,即基类中的抽象函数。

这是怎么做的。 诀窍是定义一个私有构造函数,它关闭一个自己的参数。 最初设置为null的参数充当占位符变量,您可以将其关闭以创建Task基类所需的委托。 一旦你进入构造函数体,’this’指针就可用了,所以你可以在实际的函数指针中进行补丁。

从’任务’派生:

 public abstract class DeferredActionTask : Task { private DeferredActionTask(DeferredActionTask _this) : base(_ => ((Func)_)().action(), (Func)(() => _this)) { _this = this; } protected DeferredActionTask() : this(null) { } protected abstract void action(); }; 

从’Task ‘派生:

 public abstract class DeferredFunctionTask : Task { private DeferredFunctionTask(DeferredFunctionTask _this) : base(_ => ((Func>)_)().function(), (Func>)(() => _this)) { _this = this; } protected DeferredFunctionTask() : this(null) { } protected abstract TResult function(); }; 

[编辑:简体]

这些简化版本通过直接关闭派生实例的动作函数方法,进一步减少了无关闭。 这也可以释放基类中的AsyncState ,以防您想要使用它。 现在几乎没有必要,因为你现在拥有自己的整个派生类; 因此, AsyncState不会传递给工作函数。 如果您需要它,您可以随时从基类的属性中获取它。 最后,各种可选参数现在可以传递给Task基类。

从’任务’派生:

 public abstract class DeferredActionTask : Task { private DeferredActionTask(Action _a, Object state, CancellationToken ct, TaskCreationOptions opts) : base(_ => _a(), state, ct, opts) { _a = this.action; } protected DeferredActionTask( Object state = null, CancellationToken ct = default(CancellationToken), TaskCreationOptions opts = TaskCreationOptions.None) : this(default(Action), state, ct, opts) { } protected abstract void action(); }; 

从’Task ‘派生:

 public abstract class DeferredFunctionTask : Task { private DeferredFunctionTask(Func _f, Object state, CancellationToken ct, TaskCreationOptions opts) : base(_ => _f(), state, ct, opts) { _f = this.function; } protected DeferredFunctionTask( Object state = null, CancellationToken ct = default(CancellationToken), TaskCreationOptions opts = TaskCreationOptions.None) : this(default(Func), state, ct, opts) { } protected abstract TResult function(); }; 
  private async DeferredFunctionTask WaitForStart(CancellationTokenSource c, string serviceName) { var t = await Task.Run(() => { int ret = 0; for (int i = 0; i < 500000000; i++) { //ret += i; //if (i % 100000 == 0) // Console.WriteLine(i); if (c.IsCancellationRequested) { return ret; } } return ret; }); return t; } 

错误CS1983异步方法的返回类型必须为void,Task或Task