[英]How to initilize an optional Task and Task<T> using C#?
如何选择创建多个任务并使用await Task.WhenAll(a,b,c);
执行它们await Task.WhenAll(a,b,c);
我有以下课程
public class TaskProcessor
{
private ProcessResult Result;
public TaskProcessor(ProcessResult result)
{
Result = result ?? throw new ArgumentNullException(nameof(result));
}
public async Task<ProcessResult> Process(TaskOption option)
{
// First optional task
Task<int?> typeFetcher = Task.FromResult<int?>(null);
if (option is IProcessType t)
{
typeFetcher = t.GetAsync();
}
// Second optional task
Task<int?> subtypeFetcher = Task.FromResult<int?>(null);
if (option is IProcessSubtype st)
{
subtypeFetcher = st.GetAsync();
}
// Third optional task
Task mainContent = Task.CompletedTask;
if(Result.Content != null)
{
mainContent = SetMainContentAsync(Result.Content);
}
await Task.WhenAll(typeFetcher, subtypeFetcher, mainContent);
Result.TypeId = typeFetcher.Result,
Result.SubtypeId = subtypeFetcher.Result;
return result;
}
}
我想将任务初始化为不执行任何操作的默认值。 正如你在上面的代码中看到的, typeFetcher
任务是使用Task.FromResult<int?>(null);
初始化的Task.FromResult<int?>(null);
并且当给定的TaskOption
实现IProcessType
接口时,使用自定义任务覆盖该任务。 同样的场景也适用于subtypeFetcher
任务。
对于mainContent
任务,我使用Task.CompletedTask
初始化它,当Result.Content
不为空时,我将mainContent
设置为SetMainContentAsync(Result.Content)
。 最后,我使用await Task.WhenAll(typeFetcher, subtypeFetcher, mainContent)
等待所有任务。
不要:通过调用
Task.Wait
或Task.Result
阻止异步执行。
因为我使用的是Task.FromResult<int?>(null);
初始化任务,我觉得我违反了最佳实践。 我想学习初始化可选任务的正确方法。
问题初始化返回类型的可选任务的正确方法是什么? 另外,初始化一个不向Task.CompletedTask
返回任何内容的任务是可以接受的还是有更正确的方法?
你在做什么很好。 Task.FromResult
不是Task.Result
,两者完全无关。
Task.Result
是一个实例属性,它阻塞当前线程直到任务完成,并返回任务的结果。
Task.FromResult
是一个静态方法,它返回一个微不足道的、已经完成的任务。
首先要做的事情是:以您的方式使用.Result
是“确定的”,因为您已经通过使用await Task.WhenAll
确保它已完成。 您的代码不会坐在.Result
等待结果并阻塞线程。 但由于处理异常的方式,我在引号中说“ok”。 当您使用.Result
,异常总是放在AggregateException
,您必须查看内部才能找到真正的异常。
但是,您仍然可以对已完成的任务使用await
,并且会按照您的预期抛出异常:
Result.TypeId = await typeFetcher;
至于问题的其余部分:我发现等待多个可选任务的最佳方法是使用List<Task>
并只添加所需的任务。
var taskList = new List<Task>();
但是,由于您需要使用任务的返回值,因此这很复杂。 有几种方法可以解决这个问题。 您可以使用等待的列表,但也可以像以前一样保留对每个任务本身的引用。 将Task
初始化为null
并且仅在它不是null
时才设置该值。 例如:
var taskList = new List<Task>();
Task<int?> typeFetcher = null;
if (option is IProcessType t)
{
typeFetcher = t.GetAsync();
taskList.Add(typeFetcher);
}
...
await Task.WhenAll(taskList);
if (typeFetcher != null)
{
Result.TypeId = await typeFetcher;
}
...
但是你对同一件事保留两个引用是......我不知道。 好像还可以做得更好。
另一种选择是使用设置值的本地函数并将其添加到您的任务列表中。 这样,您不必在等待后设置值。
public async Task<ProcessResult> Process(TaskOption option)
{
var taskList = new List<Task>();
// First optional task
if (option is IProcessType t)
{
// Declare a local function that sets the value
async Task TypeFetcher()
{
Result.TypeId = await t.GetAsync();
}
// Call it and add the resulting Task to the list
taskList.Add(TypeFetcher());
}
// Second optional task
Task<int?> subtypeFetcher = Task.FromResult<int?>(null);
if (option is IProcessSubtype st)
{
async Task SubtypeFetcher()
{
Result.SubtypeId = await st.GetAsync();
}
taskList.Add(SubtypeFetcher());
}
// Third optional task
if(Result.Content != null)
{
//don't need a local function here since there's no return value
taskList.Add(SetMainContentAsync(Result.Content));
}
await Task.WhenAll(taskList);
//I assume you actually declared and set result somewhere
return result;
}
我并不是说这比保留对每个Task
两个引用更有效。 只做你觉得最不容易混淆的事情。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.