64

我想使用这个 Task<TResult>构造函数。我似乎无法正确使用语法。有人可以更正我的代码吗?

另外,我是否正确地认为如果 aTask是以这种方式构造的,它就没有开始?

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

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;
}
4

6 回答 6

45
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");
于 2013-04-17T17:34:13.730 回答
6

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

public Task (Func<TResult> function);

...以这个 lambda 作为参数:

() => GetIntAsync("3")

但是,您的情况有一个特殊性:传递给构造函数的 lambda 返回 a 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
}
于 2020-05-14T15:02:39.373 回答
2

有一个类似的问题,我们希望确保任务可重用,而无需重新启动任务。Rx 救了我们的培根。您将需要 nuget 包System.Reactive

见下文GetIntTask返回一个未启动GetIntOnce的任务,并确保任务启动,并且任务的结果被缓冲并重播给任何调用者(使用Replay(1)

async Task Main()
{
    var awaitableResult = GetIntOnce();
    Console.WriteLine("Has the task started yet? No!");
    var firstResult = await awaitableResult;
    Console.WriteLine($"Awaited Once: {firstResult}");
    var secondResult = await awaitableResult;
    Console.WriteLine($"Awaited Twice: {secondResult}");
}

public static IObservable<int> GetIntOnce()
{
    return Observable.FromAsync(() =>
        {
            var t = GetIntTask();
            t.Start();
            return t;
        }).Replay(1).AutoConnect();
}

public static Task<int> GetIntTask()
{
    var t = new Task<int>(
    () =>
    {
        Console.WriteLine("Task is running!");
        return 10;
    });
    return t;
}

输出是:

Has the task started yet? No!
Task is running!
Awaited Once: 10
Awaited Twice: 10
于 2021-08-17T01:47:17.113 回答
0

我发现异步 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

于 2021-04-11T14:07:36.017 回答
-2

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

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

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

于 2021-06-10T09:51:53.970 回答
-6
   //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();
于 2013-04-17T17:41:23.200 回答