我的原始方法如下:
string DoSomeWork();
方法DoSomeWork
在其他线程上启动一些工作并返回执行ID(只是随机字符串)。稍后我可以通过给定的执行ID查询结果。重点是在作业完成之前使执行ID可用。
现在我想更改签名以返回Task,因此用户可以等待。
Task DoSomeWork();
同时我仍然需要返回执行ID(例如用于跟踪),我看到几个选项。首先是if out
参数,第二个是返回带有执行ID和任务的元组(在C#中看起来不是最好的选项),第三个是我真正要问的问题。
如果我将创建将派生自Task
类的类
public class ExtendedTask : Task
{
public string ExecutionID {get; set;}
}
这看起来不错吗?或者最好决定其他选择?
P.S。在BCL中,有一些派生自Task类。
UPDATE ,似乎我无法定义这个明确的内容。但是我需要在作业完成之前访问ExecutionID,因此我无法使用Task.Result
。
答案 0 :(得分:13)
我不会亲自扩展 Task<T>
,而是撰写。这样您就不必担心任何只返回Task<T>
的API - 您可以将任务包装起来。您可以拥有公开基础任务的属性,而对于C#5异步目的,您可以在自己的类型上实现awaiter模式 - 但我觉得创建自己的派生类型是可能弊大于利。但这主要是一种直觉。
另一种选择是反过来:将你的额外状态存储在Task.AsyncState
属性中;毕竟,这就是它的用途。这样,您可以轻松地传递任务,而不会丢失它在逻辑上属于的执行上下文。
答案 1 :(得分:11)
我建议使用Task<T>
,因为它允许您“嵌入”任务结果中的其他信息。
例如,在您的情况下,有类似的东西可能是有道理的:
class ExecutionResult
{
public int ExecutionID { get; set; }
public string Result { get; set; }
// ...
}
public Task<ExecutionResult> 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<string> 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<string>
}
这仍然可以让您访问有关您的流程的信息,以及Task
/ Task<T>
。
答案 2 :(得分:2)
如果 决定继承Task
或Task<TResult>
,您可能会遇到提供Action<Object>
或Func<Object,TResult>
代表的沮丧情绪在构造任务派生对象时,必须指定 任务的实际工作 ,并且以后无法更改。即使基类构造函数没有Start()
新创建的任务,也是如此,实际上它可能要到很晚才开始,如果有的话。
这使得在必须在其最终工作的完整细节可用之前创建实例的情况下,很难使用Task
派生类。
一个示例可能是众所周知的Task<TResult>
节点的无定形网络,这些节点处理共享目标,以便它们在 ad-hoc <中访问彼此的Result
属性/ em>方式。保证在网络中任意节点上Wait()
Result
的最简单方法是在启动任何节点之前预先构建所有节点。这巧妙地避免了尝试分析工作图依赖性的问题,并允许运行时因素确定何时,是否以及Result
值的需求顺序。
这里的问题是,对于某些节点,您可能无法提供在构造时定义工作的功能。如果创建必要的lambda函数需要从网络中的其他任务中关闭Task<TResult>
值,那么提供我们想要的Result
的{{1}}可能尚未构建。即使它恰好在预构建阶段就已经构建好了,你也不能在它上面调用Start()
,因为它可能会依赖于其他没有的节点。请记住,预构建网络的重点是避免像这样的复杂性。
好像这还不够,还有其他原因,必须使用lambda函数来提供所需的功能是不方便的。因为它作为参数传递给构造函数,所以函数无法访问最终任务实例的this
指针,这会产生丑陋的代码,特别是考虑到lambda必须在一些不相关的this
指针的范围 - 并且可能是无意的关闭。
我可以继续,但最重要的是,在派生类中定义扩展功能时,您不应该忍受运行时封闭膨胀和其他麻烦。难道没有错过多态性的全部观点吗?以正常方式定义Task
派生类的工作委托更为优雅,即基类中的抽象函数。
这是怎么做的。诀窍是定义一个私有构造函数,它关闭一个自己的参数。最初设置为null
的参数充当占位符变量,您可以将其关闭以创建Task
基类所需的委托。一旦你进入了构造函数体,那么这个&#39;这个&#39;指针可用,因此您可以在实际的函数指针中进行修补。
来自&#39;任务&#39;
public abstract class DeferredActionTask : Task
{
private DeferredActionTask(DeferredActionTask _this)
: base(_ => ((Func<DeferredActionTask>)_)().action(),
(Func<DeferredActionTask>)(() => _this))
{
_this = this;
}
protected DeferredActionTask() : this(null) { }
protected abstract void action();
};
从任务&lt; TResult&gt;&#39;
获取public abstract class DeferredFunctionTask<TResult> : Task<TResult>
{
private DeferredFunctionTask(DeferredFunctionTask<TResult> _this)
: base(_ => ((Func<DeferredFunctionTask<TResult>>)_)().function(),
(Func<DeferredFunctionTask<TResult>>)(() => _this))
{
_this = this;
}
protected DeferredFunctionTask() : this(null) { }
protected abstract TResult function();
};
[编辑:简体]
这些简化版本通过直接关闭派生实例来进一步减少无关闭。 操作或功能方法。这也可以释放基类中的AsyncState
,以防您想要使用它。这几乎没有必要,因为你现在拥有自己的整个派生类;因此,AsyncState
没有传递到工作职能部门。如果您需要它,您可以随时从基类的属性中获取它。最后,各种可选参数现在可以传递给Task
基类。
来自&#39;任务&#39;
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();
};
从任务&lt; TResult&gt;&#39;
获取public abstract class DeferredFunctionTask<TResult> : Task<TResult>
{
private DeferredFunctionTask(Func<TResult> _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<TResult>), state, ct, opts)
{
}
protected abstract TResult function();
};
答案 3 :(得分:0)
private async DeferredFunctionTask<int> WaitForStart(CancellationTokenSource c, string serviceName)
{
var t = await Task.Run<int>(() =>
{
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