如何构建一个任务而不启动它?

时间:2013-04-17 17:26:16

标签: c# task-parallel-library async-await

我想使用this Task<> constructor。我似乎无法让sntax正确,有人可以更正我的代码。

另外,我是否正确地认为如果以这种方式构建任务,它是不是已经开始了?

我认为我需要的构造函数:

Task<TResult>(Func<Object, TResult>, Object)

我的代码错误:

  

参数1:无法从“方法组”转换为“System.Func<object,int>

static void Main(string[] args)
{
    var t = new Task<int>(GetIntAsync, "3"); //error is on this line
    ...
}

static async Task<int> GetIntAsync(string callerThreadId)
{
    ...
    return someInt;
}

5 个答案:

答案 0 :(得分:26)

var t = new Task<int>(() => GetIntAsync("3").Result);

或者

var t = new Task<int>((ob) => GetIntAsync((string) ob).Result, "3");

为了避免使用lambda,你需要编写一个这样的静态方法:

private static int GetInt(object state)
{
   return GetIntAsync(((string) state)).Result;
}

然后:

var t = new Task<int>(GetInt, "3");

答案 1 :(得分:3)

要使用接受Task参数的object state构造函数,您还必须具有一个接受object参数的函数。通常这不方便。该构造函数存在的原因是为了避免在热路径中分配对象(closure)。对于正常使用,闭包的开销可以忽略不计,避免它们会毫无理由地使您的代码复杂化。因此this是您应改用的构造函数:

public Task (Func<TResult> function);

...以此lambda作为参数:

() => GetIntAsync("3")

不过,您的情况有一个特殊之处:传递给构造函数的lambda返回Task<int>。这意味着通用类型TResult被解析为Task<int>,因此您最终得到了一个嵌套任务:

var t = new Task<Task<int>>(() => GetIntAsync("3"));

启动外部任务将导致内部任务的创建。要获得最终结果,您必须两次使用await运算符,一次用于完成外部任务,一次用于完成内部任务:

static async Task Main(string[] args)
{
    var outerTask = new Task<Task<int>>(() => GetIntAsync("3"));
    //...
    outerTask.Start(); // or outerTask.RunSynchronously() to use the current thread
    //...
    Task<int> innerTask = await outerTask; // At this point the inner task has been created
    int result = await innerTask; // At this point the inner task has been completed
}

答案 2 :(得分:1)

我发现异步 lambda 效果最好,因为:

  1. 工作是通过调用方法开始的,这意味着您不需要调用 .Start,因此不可能忘记这样做。
  2. 这种方法不会“受到”内部/外部任务问题的困扰;你总是在等待正确的任务,而不是它的包装器。
Func<Task<int>> f = async () => await WorkAsync(2);

Console.WriteLine("Sleeping before start");

await Task.Delay(100);

Console.WriteLine("Waking up to start");

var result = await f();

Console.WriteLine($"The work is done and the answer is: {result}");

结果如下:

Sleeping before start
Waking up to start
Starting 2
Ending 2
The work is done and the answer is: 4

答案 3 :(得分:-1)

.net 5 中的一种新方法(实际上我不知道你什么时候可以开始这样做,我现在正在使用 .net 5)

Task<int> MyStartLaterTask = null;
//... Some Time Later
MyStartLaterTask ??= GetIntAsync(string callerThreadId);
await MyStartLaterTask;

我很欣赏这并不完全是“预先定义并稍后使用”,但据我所知,这做了同样的事情。我有一个循环,我想定义一个只在需要时运行并且只运行一次的任务。这就是这样做的,它创建了一个我可以在第一次需要时分配的任务,然后在需要输出时等待它。

答案 4 :(得分:-3)

   //creating task
   var yourTask = Task<int>.Factory.StartNew(() => GetIntAsync("3").Result);

   //...

   int result = yourTask.Result;

更新:

是的,不幸的是它确实开始了这项任务。请使用上面提到的代码:

   //creating task
   var yourTask = new Task<int>(() => GetIntAsync("3").Result);

   //...

   // call task when you want
   int result = yourTask.Start();