推迟启动一个Task<T>任务

5

我有一系列方法(带有可变数量的参数),它们返回一个Task。我想创建一个方法,在每个这些方法前后执行一些操作,通过传递Task实现。 在这个示例中,我已经简化了所有内容(删除了cancellationToken、实际处理等):

public async Task<string> GetDataString()
{
    Console.WriteLine("Executing");
    return "test";
}


public async Task<T> Process<T>(Task<T> task)
{
    Console.WriteLine("Before");
    var res = await task;
    Console.WriteLine("After");
    return res;
}

在我的主要代码中:

Task<string> task = GetDataString();
string result = await Process<string>(tasks);
Console.WriteLine(res);

控制台输出为

Executing
Before
After
test

我该怎么做才能创建任务但不实际启动它?并且仅在等待之前启动它?

我成功地创建了一个 PauseToken,如这篇文章所解释的: https://devblogs.microsoft.com/pfxteam/cooperatively-pausing-async-methods/ 但我想知道是否有更好的方法。

谢谢, Mattia


传递一个 Func<Task>。该方法在整个框架中使用。 - tia
谢谢,但我考虑过了,我不仅有GetDataString(),还可能有GetDataString2(string param)和GetDataString3(string param, int paramInt)。我该如何创建一个通用的Process(Func<Task>)方法? - Mattia Durli
函数的参数不重要。 - tia
是的,我需要一个 Process(Func<Task>),Process(Func<string, Task>) 以及 Process(Func<string, int, Task>)。 - Mattia Durli
@tia 你说得对,函数的参数并不重要,我想我没有完全理解 Func<TResult> 委托的工作原理。 - Mattia Durli
2个回答

8
您的通用 ProcessAsync 方法可以接受任务工厂作为参数:
public async Task<T> ProcessAsync<T>(Func<Task<T>> taskFactory)
{
    Console.WriteLine("Before");
    var res = await taskFactory();
    Console.WriteLine("After");
    return res;
}

通过调用工厂方法,可以在需要时创建任务。您可以控制它的创建。

下面是一个调用ProcessAsync方法的示例,将 lambda 表达式作为工厂:

var result = await ProcessAsync(() => GetDataStringAsync(arg1, arg2));

这种方法使您不必受限于没有参数的工厂方法。

为了完整起见,我应该提到,Task对象也可以使用构造函数new Task()在冷态下创建,并在稍后使用Start方法启动,但不推荐使用这种方法。


谢谢!这就是我一直在寻找的解决方案。与此同时,我自己实现了Task(),但我不喜欢它。 我不明白的是为什么Func<Task<T>>可以与GetDataStringAsync(arg1,arg2)配对,而不应该声明为Func<string,string,Task<T>>? 我误解了Func<TResult>委托的工作方式。 - Mattia Durli
GetDataStringAsync 不会直接调用,因为这样会不灵活:await ProcessAsync(GetDataStringAsync, arg1, arg2)。你需要为每组参数设置一个不同的 ProcessAsync 重载。相反,它通过捕获参数值的 lambda () => ... 间接调用。如果你想了解更多关于捕获变量和闭包的知识,请查看这里这里 - Theodor Zoulias

-1

您可以从 GetDataString 函数中删除 async 关键字,并创建一个新的任务,在 await 时执行该任务。

因此,以下代码的结果为:before、executing、test、after。

      private static async Task Start()
        {
            Task<string> task = GetDataString();
            string result = await Process<string>(task);
            Console.WriteLine(result);

            Console.ReadLine();
        }


        public Task<string> GetDataString()
        {
            return new TaskFactory(TaskScheduler.Default).StartNew(() =>
            {
                Console.WriteLine("Executing");
                return "test";
            });
        }


        public async Task<T> Process<T>(Task<T> task)
        {
            Console.WriteLine("Before");
            var res = await task;
            Console.WriteLine("After");
            return res;
        }

调用 GetDataString 仍然返回一个热任务,这不是 OP 想要的。 - JSteward

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接