我在_cache = new int[N+1, N+1]
类型中定义了可变数量的任务,因此我可以定义任务但不会自动启动它们。比方说,例如:
Func<Task>
以这种方式定义的一些任务会返回值。我需要一种方法将这些任务的结果“传递”给后续任务的参数,并以相同的方式定义。所以,我想写一下:
Func<Task> task1Func = () => DoTask1Async();
问题在于Func<Task> task2Func = () => DoTask2Async(task1Func.Invoke().Result));
部分再次启动task1任务,这是不好的。
我无法将输入重构为链延续,例如:
task1Func.Invoke()
因为我需要在代码中知道链中的每个任务(即DoTask1Async / DoTask2Async)完成运行其他代码的点。另一个问题是我不知道如何计算“链”中涉及的任务数量。
我无法存储完成的任务结果,因为需要以声明方式定义任务,如Func<Task> funcTasks = () => DoTask1Async().ContinueWith(t => DoTask2Async(t.Result));
和task1Func
。
以定义的顺序处理任务,每个任务在处理下一个任务之前完成。任务结果仅用于后续任务。
我希望这一切都有道理。
非常感谢您的任何帮助。
编辑:
在此处使用task2Func
响应对可运行代码的请求。创建一个新的WPF项目并将一个按钮添加到默认的Grid。然后清除MainWindow.xaml.cs文件并粘贴下面的内容。这是我真实项目中最接近的代码,同时删除了与问题无关的所有内容。
task1Func.Invoke().Result
}
我希望这会有所帮助。再次感谢您的帮助。
答案 0 :(得分:0)
使用Microsoft的Reactive Framework,您可以执行以下操作:
var query =
from r1 in Observable.FromAsync(() => DoTask1Async())
from r2 in Observable.FromAsync(() => DoTask2Async(r1))
select new { r1, r2 };
await query;
答案 1 :(得分:0)
您可以将这些延迟的异步操作表示为嵌套任务(Task<Task>
)。外部任务将代表操作的开始,内部任务将代表操作的完成。内部任务是通过unwrapping外部任务获得的:
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);
});
外部任务开始冷淡的生活。您必须调用其方法Start
或RunSynchronously
才能变热并开始运行。这两种方法只能调用一次。他们第二次抛出InvalidOperationException
,因此,为了保持从多个位置在不同时间启动任务的灵活性,您必须预料并吞下此异常。
为了将这些操作存储在List
中,您必须分别存储外部任务和内部任务,因为外部任务是您启动的内容,内部任务是您等待的内容。将它们放入tuples ValueTuple<Task, Task>
很方便:
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;
}
之所以允许这样做,是因为泛型类Task<TResult>
派生自基类Task
。
替代:代替嵌套的Task<Task>
,另一种选择是使用Lazy<Task>
。类Lazy
保证包装的任务仅被实例化一次,并且具有线程安全性。虽然有一个困难。我们不能将Lazy<Task>
和Lazy<Task<T>>
的实例放在同一List
中。 C#编译器认为它们是完全不同的类。因此,让我们制作两个LazyTask
包装器,其中一个包装器是由非包装器衍生而来的,它们模仿了Task<T>
和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();
}
有了这些类,实现就比“嵌套任务”方法干净得多。无需手动启动任务和吞下异常。根据需要自动创建任务。
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;
}