Действителен ли возврат"? Или лучше вернуть" Task.FromResult (MyObject) " - PullRequest
1 голос
/ 03 апреля 2019

Мой код в порядке, но мне интересно, какой стиль лучше, как вы увидите, я играю с асинхронными методами.

Позвольте мне установить контекст:

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

этот фрагмент кода работает очень хорошо с этим методом:

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

В предыдущем примере вы можете увидеть Task.FromResult () сразу после ключевого слова await.Метод 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 Ответ

5 голосов
/ 03 апреля 2019

Я думаю, что вы путаете параллелизм с асинхронностью и не делаете ни одного правильно.

Если ваш метод возвращает Task.FromResult, то он не асинхронный. Если вы хотите иметь асинхронный код, сфокусируйтесь на вводе / выводе, например, асинхронно выполняйте ввод / вывод, чтобы загрузить данные файла с диска, а затем (синхронно) проанализировать их как XML.

Проблемы Parallel.ForEach более опасны. Во-первых, вы не можете использовать async методы с Parallel.ForEach; ваш код просто работает, потому что ваши async методы не асинхронные. Кроме того, вы не можете использовать не потоковые методы, такие как List<T>.Add, из параллельного кода. Так что почти весь код, использующий Parallel.ForEach, неверен. Но вам, вероятно, все равно не нужно Parallel.ForEach.

Если вы хотите выполнить асинхронный параллелизм, вам просто нужны LINQ Select и await Task.WhenAll. Если вы хотите настроить тип конвейера, который может выполнять параллельную обработку, то вы можете либо использовать поток данных TPL, либо использовать Parallel.ForEach только с синхронным кодом после выполнения асинхронных частей.

...