有效返回“ Task <任务<myobject >>”吗?或者最好返回“ Task.FromResult(MyObject)”

时间:2019-04-03 03:02:11

标签: c# asynchronous async-await

我的代码还可以,但是我想知道哪种风格更好,如何看待,我正在使用异步方法。

让我建立上下文:

Parallel.ForEach(xmlAnimalList, async xml =>
        {
            taskList.Add(await Task.FromResult(ReadAnimalXML(xml, token)));
        });

此代码段可通过此方法很好地工作:

public async Task<Animal> ReadAnimalXML(string filename, CancellationToken token)

在前面的示例中,您可以在await关键字之后看到 Task.FromResult()。 ReadAnimalXML方法仅返回:

return new Animal();

第二个示例是:

Parallel.ForEach(xmlAnimalList, async xml =>
        {
            taskList.Add(await ReadAnimalXML2(xml, token));
        });

这次,ReadAnimalXML2方法返回以下内容:

public async Task<Task<Animal>> ReadAnimalXML2(string filename, CancellationToken token)
    {
        return Task.FromResult(new Animal());
    }

但是!

第二种方法ReadAnimalXML2(对我来说似乎很奇怪)返回一个

Task<Task<Animal>>

任务内部的任务。

这就是为什么我返回 Task.FromResult(new Animal()); 的原因 否则它将无法正常工作。两种方法都可以,但是更好。您能否分享您的答案,并解释原因?

谢谢您输入此问题。 编码很有趣!

public async Task<IEnumerable<Animal>> ReadXMLFromFolderAsync(string folderPath, CancellationToken token)
    {
        if (!Directory.Exists(folderPath))
        {
            return new List<Animal>();
        }

        List<Task<Animal>> taskList = new List<Task<Animal>>();

        List<string> xmlAnimalList = Directory.GetFiles(folderPath, "*.xml").ToList();

        Parallel.ForEach(xmlAnimalList, async xml =>
        {
            taskList.Add(await Task.FromResult(ReadAnimalXML(xml, token)));
        });

        return await Task.WhenAll(taskList);
    }

    public async Task<Animal> ReadAnimalXML(string filename, CancellationToken token)
    {
        XDocument document = XDocument.Load(filename);

        IEnumerable<XElement> ADN = await Task.Run(() => 
            document.Descendants("ADN").Where(adn => adn.Name.LocalName == "Dinosaur"), token);

        //populate the animal object

        return new Animal();
    }

    public async Task<IEnumerable<Animal>> ReadXMLFromFolderAsync2(string folderPath, CancellationToken token)
    {
        if (!Directory.Exists(folderPath))
        {
            return new List<Animal>();
        }

        List<Task<Animal>> taskList = new List<Task<Animal>>();

        List<string> xmlAnimalList = Directory.GetFiles(folderPath, "*.xml").ToList();

        Parallel.ForEach(xmlAnimalList, async xml =>
        {
            taskList.Add(await ReadAnimalXML2(xml, token));
        });

        return await Task.WhenAll(taskList);
    }

    public async Task<Task<Animal>> ReadAnimalXML2(string filename, CancellationToken token)
    {
        XDocument document = XDocument.Load(filename);

        IEnumerable<XElement> ADN = await Task.Run(() => document
            .Descendants("ADN")
            .Where(adn => adn
                .Name
                .LocalName == "Dinosaur")
                , token);

        //populate the animal object

        return Task.FromResult(new Animal());
    }

1 个答案:

答案 0 :(得分:5)

我认为您正在将并行性与异步性混淆,并且两者都不正确。

如果您的方法返回Task.FromResult,则它不是异步的。如果要使用异步代码,请专注于I / O,例如,异步执行I / O以从磁盘加载文件数据,然后(同步)将其解析为XML。

Parallel.ForEach问题更加危险。首先,不能将async方法与Parallel.ForEach一起使用;您的代码恰好起作用,因为您的async方法不是异步的。另外,您不能在并行代码中使用非线程安全方法,例如List<T>.Add。因此,几乎所有使用Parallel.ForEach的代码都是错误的。但是您可能仍然不需要Parallel.ForEach

如果要进行异步并发,则只需要LINQ的Selectawait Task.WhenAll。如果要建立一种可以并行处理的管道,那么在完成异步部分之后,可以使用TPL Dataflow,也可以对同步代码使用Parallel.ForEach 。 / p>