C# 如何将任务结果传递给不使用continuations的其他任务

C# 如何将任务结果传递给不使用continuations的其他任务,c#,asynchronous,async-await,task-parallel-library,task,C#,Asynchronous,Async Await,Task Parallel Library,Task,我在Func类型中定义了数量可变的任务,因此我可以定义任务,但不能让它们自动启动。比如说: Func<Task> task1Func = () => DoTask1Async(); 正如我需要在代码中知道的那样,链中的每个任务(即DoTask1Async/DoTask2Async)完成运行其他代码的时间点。另一个问题是,我不知道如何计算“链”中涉及多少任务 我无法在任务完成时存储任务结果,因为任务需要以声明方式定义,如task1Func和task2Func 任务按定义的顺序处

我在
Func
类型中定义了数量可变的任务,因此我可以定义任务,但不能让它们自动启动。比如说:

Func<Task> task1Func = () => DoTask1Async();
正如我需要在代码中知道的那样,链中的每个任务(即DoTask1Async/DoTask2Async)完成运行其他代码的时间点。另一个问题是,我不知道如何计算“链”中涉及多少任务

我无法在任务完成时存储任务结果,因为任务需要以声明方式定义,如
task1Func
task2Func

任务按定义的顺序处理,每个任务在处理下一个任务之前完成。任务的结果仅用于后续任务

编辑 使用
task1Func.Invoke().Result
响应可运行代码的请求。创建一个新的WPF项目,并向默认网格添加一个按钮。然后清除MainWindow.xaml.cs文件并粘贴到下面的内容中。这是最接近我真实项目的代码,同时删除了所有与问题无关的内容

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApplication2
{

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        // Declare first task that returns a result
        Func<Task<int>> deferredTask1 = () => DoTask1Async();
        // Declare second task using result of first.
        // BUT this isn't good as it calls deferredTask1 AGAIN, so need another approach.
        Func<Task> deferredTask2 = () => DoTask2Async(deferredTask1.Invoke().Result);

        var item1 = new MyItem() { MyDeferredTask = deferredTask1 };
        var item2 = new MyItem() { MyDeferredTask = deferredTask2 };

        List<MyItem> items = new List<MyItem>();

        items.Add(item1);
        items.Add(item2);

        await DoTasksAsync(items);

    }

    public static async Task<int> DoTask1Async()
    {
        return await Task<int>.Run(() => { return 3000; });
    }

    public static async Task DoTask2Async(int delay)
    {
        await Task.Delay(delay);
    }

    private async Task DoTasksAsync(IEnumerable<MyItem> items)
    {
        foreach (var item in items)
        {
            await item.MyDeferredTask();
        }
    }

    private class MyItem
    {
        public Func<Task> MyDeferredTask { get; set; }
    }
}
}
使用系统;
使用System.Collections.Generic;
使用System.Threading.Tasks;
使用System.Windows;
命名空间WpfApplication2
{
/// 
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
}
专用异步无效按钮\u单击(对象发送方,路由目标)
{
//声明返回结果的第一个任务
Func deferredTask1=()=>DoTask1Async();
//使用第一个任务的结果声明第二个任务。
//但这并不好,因为它再次调用了deferredTask1,所以需要另一种方法。
Func deferredTask2=()=>DoTask2Async(deferredTask1.Invoke().Result);
var item1=new MyItem(){MyDeferredTask=deferredTask1};
var item2=new MyItem(){MyDeferredTask=deferredTask2};
列表项=新列表();
增加(第1项);
增加(第2项);
等待DoTasksAsync(项目);
}
公共静态异步任务DoTask1Async()
{
return wait Task.Run(()=>{return 3000;});
}
公共静态异步任务DoTask2Async(int延迟)
{
等待任务。延迟(延迟);
}
专用异步任务DoTasksAsync(IEnumerable项)
{
foreach(项目中的var项目)
{
等待项。MyDeferredTask();
}
}
私有类MyItem
{
公共函数MyDeferredTask{get;set;}
}
}
}

使用Microsoft的反应式框架,您可以执行以下操作:

var query =
    from r1 in Observable.FromAsync(() => DoTask1Async())
    from r2 in Observable.FromAsync(() => DoTask2Async(r1))
    select new { r1, r2 };

await query;

您可以将这些延迟的异步操作表示为嵌套任务(
Task
)。外部任务表示操作的开始,内部任务表示操作的完成。内部任务由外部任务获得:

Task<Task<int>> task1 = new Task<Task<int>>(async () =>
{
    return await DoTask1Async();
});

Task<Task> task2 = new Task<Task>(async () =>
{
    try { task1.RunSynchronously(); } catch (InvalidOperationException) { }
    int task1Result = await task1.Unwrap();
    await DoTask2Async(task1Result);
});
这是允许的,因为泛型类派生自基类
Task


备选方案:替代嵌套的
任务
s,另一个选项是使用
Lazy
s。该类保证包装的任务只实例化一次,并且具有线程安全性。不过,有一个困难。我们不能将
Lazy
Lazy
的实例放在同一个
列表中。C#编译器认为它们是完全不同的类。因此,让我们制作两个
LazyTask
包装器,其中泛型包装器派生自非泛型包装器,模仿
Task
Task
之间的关系:

class LazyTask
{
    private readonly Lazy<Task> _lazyTask;

    public LazyTask(Func<Task> function) => _lazyTask = new Lazy<Task>(function);

    public Task Task => _lazyTask.Value;

    public TaskAwaiter GetAwaiter() => this.Task.GetAwaiter(); // Make it awaitable
}

class LazyTask<T> : LazyTask
{
    public LazyTask(Func<Task<T>> function) : base(function) { }

    public new Task<T> Task => (Task<T>)base.Task;

    public T Result => this.Task.GetAwaiter().GetResult();

    public new TaskAwaiter<T> GetAwaiter() => this.Task.GetAwaiter();
}
类懒散任务
{
私人只读懒任务;
公共懒散任务(Func函数)=>u懒散任务=新懒散任务(函数);
公共任务任务=>\u lazyTask.Value;
public TaskAwaiter GetAwaiter()=>this.Task.GetAwaiter();//使其可等待
}
类LazyTask:LazyTask
{
公共懒散任务(Func函数):基(函数){}
公共新任务Task=>(Task)base.Task;
public T Result=>this.Task.GetAwaiter().GetResult();
public new TaskAwaiter GetAwaiter()=>this.Task.GetAwaiter();
}
有了这些类,实现比“嵌套任务”方法更干净。无需手动启动任务和排除异常。任务是根据需要自动创建的

LazyTask<int> task1 = new LazyTask<int>(async () =>
{
    return await DoTask1Async();
});

LazyTask task2 = new LazyTask(async () =>
{
    await DoTask2Async(await task1);
});

List<LazyTask> tasks = new List<LazyTask>() { task1, task2 };

foreach (var task in tasks)
{
    await task;
}
LazyTask task1=新的LazyTask(异步()=>
{
返回wait wait DoTask1Async();
});
LazyTask task2=新的LazyTask(异步()=>
{
wait DoTask2Async(wait task1);
});
列表任务=新列表(){task1,task2};
foreach(任务中的var任务)
{
等待任务;
}

能否将签名从
Func
更改为
Func
?@ScottChamberlain,是的,这是可能的。我想我们需要一个更完整的示例,说明如何设置多个任务并将其链接。使用示例中的
task1Func.Invoke().Result
版本为我们创建一个。这将极大地帮助我们向我们展示如何更好地完成任务。@ScottChamberlain,请参阅我上面的编辑,以回应您的请求。希望这有帮助。你确定你是从正确的方向来的吗?这可能是一个更好的起点吗?
List<(Task, Task)> tasks = new List<(Task, Task)>()
{
    (task1, task1.Unwrap()),
    (task2, task2.Unwrap()),
};

foreach (var (outerTask, innerTask) in tasks)
{
    try { outerTask.RunSynchronously(); } catch (InvalidOperationException) { }
    await innerTask;
}
class LazyTask
{
    private readonly Lazy<Task> _lazyTask;

    public LazyTask(Func<Task> function) => _lazyTask = new Lazy<Task>(function);

    public Task Task => _lazyTask.Value;

    public TaskAwaiter GetAwaiter() => this.Task.GetAwaiter(); // Make it awaitable
}

class LazyTask<T> : LazyTask
{
    public LazyTask(Func<Task<T>> function) : base(function) { }

    public new Task<T> Task => (Task<T>)base.Task;

    public T Result => this.Task.GetAwaiter().GetResult();

    public new TaskAwaiter<T> GetAwaiter() => this.Task.GetAwaiter();
}
LazyTask<int> task1 = new LazyTask<int>(async () =>
{
    return await DoTask1Async();
});

LazyTask task2 = new LazyTask(async () =>
{
    await DoTask2Async(await task1);
});

List<LazyTask> tasks = new List<LazyTask>() { task1, task2 };

foreach (var task in tasks)
{
    await task;
}