Как использовать asyn c методы - PullRequest
0 голосов
/ 20 марта 2020

В настоящее время у меня проблемы с использованием правильных методов asyn c. Я могу что-то неправильно понять ...

В моем текущем проекте у меня есть несколько асин c методов, которые должны работать параллельно, но они этого не делают. Поэтому я создал небольшой пример проекта для воспроизведения ошибки.

class Program
    {
        static void Main(string[] args)
        {
            TaskAwaiter taskAwaiter = SomeParallelWork().GetAwaiter();
            taskAwaiter.GetResult();
        }

        private static async Task SomeParallelWork()
        {
            Console.WriteLine(DateTime.Now + ": Start Parallel Work");
            Task<string> task1 = HeavyDBWork(6000);
            Task<string> task2 = HeavyDBWork(3000);
            Task<string> task3 = HeavyDBWork(5000);
            Console.WriteLine(DateTime.Now + ": All Parallel Work started");
            await Task.WhenAll(new Task<string>[] { task1, task2, task3 });
            Console.WriteLine(DateTime.Now + ": All Parallel Work done");
        }


        private static async Task<string> HeavyDBWork(int timeToWork)
        {
            Console.WriteLine(DateTime.Now + ": Start with heavy db work load: " + timeToWork);
            await Task.Delay(timeToWork);
            Console.WriteLine(DateTime.Now + ": End of heavy db work load: " + timeToWork);
            return "SomeReturnValue";
        }
    }

Вывод такой, какой я ожидал:

20.03.2020 10:50:28: Start Parallel Work
20.03.2020 10:50:28: Start with heavy db work load: 6000
20.03.2020 10:50:28: Start with heavy db work load: 3000
20.03.2020 10:50:28: Start with heavy db work load: 5000
20.03.2020 10:50:28: All Parallel Work started
20.03.2020 10:50:31: End of heavy db work load: 3000
20.03.2020 10:50:33: End of heavy db work load: 5000
20.03.2020 10:50:34: End of heavy db work load: 6000
20.03.2020 10:50:34: All Parallel Work done

Сначала создаются все задачи, а затем выполняются в параллельно. Но это не то, что я испытал в своем проекте ...

, поэтому я расширил свой пример еще более "реальной" работой: рекурсивный алгоритм Фибоначчи

class Program
    {
        static void Main(string[] args)
        {
            TaskAwaiter taskAwaiter = SomeParallelWork().GetAwaiter();
            taskAwaiter.GetResult();
        }

        private static async Task SomeParallelWork()
        {
            Console.WriteLine(DateTime.Now + ": Start Parallel Work");
            Task<long> fibuTask = CPUWork(40);
            Task<string> task1 = HeavyDBWork(6000);
            Task<string> task2 = HeavyDBWork(3000);
            Task<string> task3 = HeavyDBWork(5000);
            Console.WriteLine(DateTime.Now + ": All Parallel Work started");
            await Task.WhenAll(new Task<string>[] { task1, task2, task3 });
            await Task.WhenAll(new Task<long>[] { fibuTask });
            Console.WriteLine(DateTime.Now + ": All Parallel Work done");
        }


        private static async Task<string> HeavyDBWork(int timeToWork)
        {
            Console.WriteLine(DateTime.Now + ": Start with heavy db work load: " + timeToWork);
            await Task.Delay(timeToWork);
            Console.WriteLine(DateTime.Now + ": End of heavy db work load: " + timeToWork);
            return "SomeReturnValue";
        }

        private static async Task<long> CPUWork(int i)
        {
            Console.WriteLine(DateTime.Now + ": Start Fibu of " + i);
            long fibu = Fibu(i);
            Console.WriteLine(DateTime.Now + ": End Fibu of " + i);
            return await Task.FromResult<long>(fibu);
        }


        private static long Fibu(int i)
        {
            if(i==1||i==2)
            {
                return (long)1;
            } else
            {
                return Fibu(i-2) + Fibu(i - 1);
            }
        }
    }

Вывод:

20.03.2020 10:56:09: Start Parallel Work
20.03.2020 10:56:09: Start Fibu of 40
20.03.2020 10:56:10: End Fibu of 40
20.03.2020 10:56:10: Start with heavy db work load: 6000
20.03.2020 10:56:10: Start with heavy db work load: 3000
20.03.2020 10:56:10: Start with heavy db work load: 5000
20.03.2020 10:56:10: All Parallel Work started
20.03.2020 10:56:13: End of heavy db work load: 3000
20.03.2020 10:56:15: End of heavy db work load: 5000
20.03.2020 10:56:16: End of heavy db work load: 6000
20.03.2020 10:56:16: All Parallel Work done

Теперь задача «Последовательность Фибоначчи» не просто создается первой, но и ожидает того, как будут созданы другие задачи. Я ожидал бы, что код создаст все задачи и выполнит их параллельно. Что я не так?

Спасибо за ваши ответы!

Ответ:

Благодаря Джонатану, Лассе и Теодору я смог исправить свой код пример.

Таким образом, новый пример выполняет три алгоритма Фибуначи асинхронно.

class Program
    {
        static void Main(string[] args)
        {
            TaskAwaiter taskAwaiter = SomeParallelWork().GetAwaiter();
            taskAwaiter.GetResult();
        }

        private static async Task SomeParallelWork()
        {
            Console.WriteLine(DateTime.Now + ": Start Parallel Work");
            Task<long> fibuTask1 = CPUWork(45);
            Task<long> fibuTask2 = CPUWork(40);
            Task<long> fibuTask3 = CPUWork(40);
            Console.WriteLine(DateTime.Now + ": All Parallel Work started");

            //await Task.WhenAll(new Task<string>[] { task1, task2, task3 });
            await Task.WhenAll(new Task<long>[] { fibuTask1, fibuTask2, fibuTask3 });
            Console.WriteLine(DateTime.Now + ": All Parallel Work done");
        }


        private static async Task<string> HeavyDBWork(int timeToWork)
        {
            Console.WriteLine(DateTime.Now + ": Start with heavy db work load: " + timeToWork);

            await Task.Delay(timeToWork);
            Console.WriteLine(DateTime.Now + ": End of heavy db work load: " + timeToWork);
            return "SomeReturnValue";
        }

        private static async Task<long> CPUWork(int i)
        {
            Console.WriteLine(DateTime.Now + ": Start Fibu of " + i);

            Func<long> fibuFunc = () => Fibu(i);
            Task<long> fibuTask = Task.Run(fibuFunc);

            long fibu = await fibuTask;


            Console.WriteLine(DateTime.Now + ": End Fibu of " + i);
            return fibu;
        }


        private static long Fibu(int i)
        {
            if(i==1||i==2)
            {
                return (long)1;
            } else
            {
                return Fibu(i-2) + Fibu(i - 1);
            }
        }
    }

Вывод:

20.03.2020 11:59:39: Start Parallel Work
20.03.2020 11:59:39: Start Fibu of 45
20.03.2020 11:59:39: Start Fibu of 40
20.03.2020 11:59:39: Start Fibu of 40
20.03.2020 11:59:39: All Parallel Work started
20.03.2020 11:59:41: End Fibu of 40
20.03.2020 11:59:41: End Fibu of 40
20.03.2020 11:59:53: End Fibu of 45
20.03.2020 11:59:53: All Parallel Work done

1 Ответ

2 голосов
/ 20 марта 2020

Ваш метод CPUWork фактически будет работать синхронно, даже если он объявлен как async.

Asyn c Методы возвращают Task только при обнаружении первого await, и Task.FromResult просто оборачивает значение в уже заполненный Task, поэтому использование await сразу развернет значение и вернет его.

...