【问题标题】:Eric Lippert's Asynchronous Programming exampleEric Lippert 的异步编程示例
【发布时间】:2017-08-25 05:42:52
【问题描述】:

我正在尝试使用 Eric Lippert 在 2011 年的 Asynchronous Programming - Easier Asynchronous Programming with the New Visual Studio Async CTP 中的示例了解 C# 中异步编程的基础知识。

我已经为上一个代码示例中引用的方法填写了存根,我希望在命令行上看到的是:

Start obtain order
Finish obtain order
Start obtain ingredients
Start obtain recipe
Finish obtain ingredients
Finish obtain recipe
Start recipe prepare meal
Finish recipe prepare meal
Diner receives meal

但相反,我看到每个方法都被依次调用 - 似乎没有什么是异步的。

我的代码有错误,还是我误解了它的工作原理?

非常感谢您的指导。

namespace AsyncAwait
    {
    using System;
    using System.Threading.Tasks;

    internal class Program
    {
        internal class Order
        {
        }

        internal class Ingredients
        {
        }

        internal class Recipe
        {
            internal async Task<Meal> PrepareAsync(Ingredients ingredients)
            {
                Console.WriteLine("Start recipe prepare meal");
                await Task.Delay(4 * 1000);
                Console.WriteLine("Finish recipe prepare meal");
                return new Meal();
            }
        }

        internal class Meal
        {
        }

        private class Diner
        {
            internal void Give(Meal meal)
            {
                Console.WriteLine("Diner receives meal");
            }
        }

        async private static Task<Order>ObtainOrderAsync(Diner diner)
        {
            Console.WriteLine("Start obtain order");
            await Task.Delay(3 * 1000);
            Console.WriteLine("Finish obtain order");
            return new Order();
        }

        async private static Task<Ingredients>ObtainIngredientsAsync(Order order)
        {
            Console.WriteLine("Start obtain ingredients");
            await Task.Delay(2 * 1000);
            Console.WriteLine("Finish obtain ingredients");
            return new Ingredients();
        }

        async private static Task<Recipe>ObtainRecipeAsync(Order order)
        {
            Console.WriteLine("Start obtain recipe");
            await Task.Delay(5 * 1000);
            Console.WriteLine("Finish obtain recipe");
            return new Recipe();
        }

        async private static void ServeBreakfast(Diner diner)
        {
            Order order = await ObtainOrderAsync(diner);
            Ingredients ingredients = await ObtainIngredientsAsync(order);
            Recipe recipe = await ObtainRecipeAsync(order);
            Meal meal = await recipe.PrepareAsync(ingredients);
            diner.Give(meal);
        }

        static void Main(string[] args)
        {
            Diner diner = new Diner();
            ServeBreakfast(diner);
            Console.ReadLine();
        }
    }
}

【问题讨论】:

  • 这不可能是所有代码...
  • @ErikPhilips 您认为缺少什么?代码运行良好。
  • 您正在依次等待每个任务。仔细看看ServeBreakfast
  • 为什么你认为你应该能够,比如说,在你有食谱或配料之前准备好食谱?这是没有意义的。您需要准备它们的配方和成分,这在代码中表示。仅仅因为工作发生异步并不意味着它们可以按任何顺序发生。这只是意味着他们在工作时不会阻塞线程。
  • 请记住,await 并不意味着“异步运行此代码”。到达等待时,代码已经异步运行! Await 的意思是“在这个任务完成之前我不能继续,所以在你等待的时候做点别的事情”。

标签: c# asynchronous


【解决方案1】:

每个Async 方法都返回一个可以被awaited 的任务。 当你调用xxxAsync 方法时,你启动了异步操作,而且,当你await 同一行的结果(即任务)时,你告诉程序“我需要这个方法的结果才能继续”。

我在您的示例中看到的唯一可以并行运行的方法是ObtainIngredientsAsyncObtainRecipeAsync。所有其他方法都需要以前方法的结果。

如果您希望这些提到的方法并行运行,请先调用这两个方法而不使用await,然后再调用它们await,然后再继续。

async private static void ServeBreakfast(Diner diner)
        {
            Order order = await ObtainOrderAsync(diner);
            Task<Ingredients> ingredientsTask = ObtainIngredientsAsync(order);
            Task<Recipe> recipeTask = ObtainRecipeAsync(order);
            Ingredients ingredients = await ingredientsTask;
            Recipe recipe = await recipeTask
            Meal meal = await recipe.PrepareAsync(ingredients);
            diner.Give(meal);
        }

【讨论】:

    【解决方案2】:

    你正在等待每个任务,一个接一个:

    Order order = await ObtainOrderAsync(diner);
    Ingredients ingredients = await ObtainIngredientsAsync(order);
    Recipe recipe = await ObtainRecipeAsync(order);
    Meal meal = await recipe.PrepareAsync(ingredients);
    diner.Give(meal);
    

    当您await 一个任务时,您会停止执行该方法,直到任务完成。

    因此,您正在为任务定义严格的逐一顺序:在前一个任务完成之前,您不会开始任务。

    这是一个示例,其中两个任务并行完成,这应该会为您提供您期望的输出。

    为了(希望)使代码更清晰,我已将您的所有等待分解为单独的语句,用于创建任务并等待它们。

    async private static void ServeBreakfast(Diner diner)
    {
        // first task is to get the order
        Task<Order> getOrder = ObtainOrderAsync(diner);
    
        // we need to wait for the order, then we 
        // can get the ingredients and recipe in parallel
        Order order = await getOrder;
    
        // ### Change from your logic: Here we're starting two tasks in parallel. ###
        Task<Ingredients> getIngredients = ObtainIngredientsAsync(order);
        Task<Recipe> getRecipe = ObtainRecipeAsync(order);
    
        // once we have both the above we can make the meal
        Ingredients ingredients = await getIngredients;
        Recipe recipe = await getRecipe;
        Task<Meal> getMeal = recipe.PrepareAsync(ingredients);
    
        // when the meal is ready, give it to the diner
        diner.Give(await getMeal);
    }
    

    【讨论】:

    • 你的分解版本不完全一样,ObtainRecipeAsync 直到ObtainIngredientsAsync 返回的任务完成后才开始调用。您可能希望更清楚地表明您正在更改新示例中代码的行为。
    猜你喜欢
    • 2011-07-05
    • 1970-01-01
    • 2011-01-07
    • 2018-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多