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

时间:2012-02-13 17:49:55

标签: c# .net asynchronous task-parallel-library async-ctp

我的原始方法如下:

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

4 个答案:

答案 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)

如果 决定继承TaskTask<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