C#异步方法一直调用Main

时间:2015-05-26 09:26:19

标签: c# async-await

有人可以澄清这个例子,当然,这个例子不起作用:

class Program
{
    static void Main(string[] args)//main cant' be async
    {
        int res = test();//I must put await here

        Console.WriteLine("END");
    }

    public async static Task<int> test()
    { //why can't I make it just: public int test()??
        int a1, a2, a3, a4;

        a1 = await GetNumber1();
        a2 = await GetNumber2();
        a3 = await GetNumber3();

        a4 = a1 + a2 + a3;
        return a4;
    }

    public static async Task<int> GetNumber1()
    {
        await Task.Run(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("GetNumber1");
                    System.Threading.Thread.Sleep(100);
                }
            });
        return 1;
    }

我正在努力收集&#34;使用&#34;等待&#34;来自GenNumberX方法的值。我想制作方法&#34; test&#34;某种程度上没有异步。我不明白为什么当我使用await获取值时,测试必须是异步的。这个例子让我在每个使用异步的方法上编写异步,当我向上钻取Main时,我不能让它异步?

如何编写真实世界的例子:

public bool ReserveAHolliday()
{
        bool hotelOK = await ReserveAHotel();//HTTP async request
        bool flightOK = await ReserveFlight();////HTTP async request
        bool result = hotelOK && flightOK;
        return result;
}

如何使方法 ReserveAHolliday 同步? 我错过了什么或者不了解async-await机制的使用?

6 个答案:

答案 0 :(得分:2)

下面的

是一个完整的例子。您可以同时运行ReserverAHoliday(bool r = ReserveAHolliday()。结果;)和异步(只需从MAIN调用ReserveAHolliday();)(取决于您评论的哪一行)。并且您可以看到效果(&#34; END&#34;在预订完成之前/之后打印)。 我更喜欢await Task.WhenAll()方法,它更具可读性。 另请注意,在GetNumber1中使用等待Task.Delay(100)而不是Thread.sleep是首选。

    class Program
{
    static void Main(string[] args)//main cant' be async
    {
        //int res = test().Result;//I must put await here
        bool r = ReserveAHolliday().Result; //this will run Synchronously.
        //ReserveAHolliday(); //this option will run aync : you will see "END" printed before the reservation is complete.
        Console.WriteLine("END");
        Console.ReadLine();
    }

    public async static Task<int> test()
    { //why can't I make it just: public int test()??
        //becuase you cannot use await in synchronous methods. 
        int a1, a2, a3, a4;

        a1 = await GetNumber1();
        a2 = await GetNumber1();
        a3 = await GetNumber1();

        a4 = a1 + a2 + a3;
        return a4;
    }

    public static async Task<int> GetNumber1()
    {
        //await Task.Run(() =>
        //    {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("GetNumber1");
                    await Task.Delay(100); // from what I read using Task.Delay is preferred to using  System.Threading.Thread.Sleep(100);
                }
        //    });
        return 1;
    }

    public async static Task<bool> ReserveAHolliday()
    {
        //bool hotelOK = await ReserveAHotel();//HTTP async request
        //bool flightOK = await ReserveAHotel();////HTTP async request
        var t1 = ReserveAHotel("FirstHotel");
        var t2 = ReserveAHotel("SecondHotel");
        await Task.WhenAll(t1, t2);
        bool result = t1.Result && t1.Result;// hotelOK && flightOK;
        return result;
    }
    public static async Task<bool> ReserveAHotel(string name)
    {
        Console.WriteLine("Reserve A Hotel started for "+ name);
        await Task.Delay(3000);
        if (name == "FirstHotel") 
            await Task.Delay(500); //delaying first hotel on purpose.
        Console.WriteLine("Reserve A Hotel done for " + name);
        return true;
    }
}

答案 1 :(得分:1)

您的异步需要从某处开始,并且由于您的程序有一个起点,因此此时它是一种同步方法。大多数async从我们想要称之为async void的方式开始,这些方法基本上都是火灾和遗忘方法。它开始一项任务,并不关心它返回的内容。如果您需要在同步方法中等待某事,可以在任务上使用.Wait()方法,或使用.Result来获取任务的结果。

对于您的真实世界示例,如果您想同时运行这两个任务,则需要执行以下操作:

public async Task<bool> ReserveAHoliday()
{
        //Initialize and start this task
        Task<bool> hotelTask = ReserveAHotel();//HTTP async request
        //Initialize and start this task
        Task<bool> flightTask = ReserveFlight();////HTTP async request
        //Await until hotel task is done, and get the bool
        bool hotelOK = await hotelTask;
        //Await until flight task is done (This might have already finished while the hotel was grabbing, and get the bool
        bool flightOK = await flightTask;
        bool result = hotelOK && flightOK;
        //Return the final result
        return result;
}

我强烈建议您观看this video。它很好地介绍了Async如何工作,并且可以在异步的精彩世界中启动你。

答案 2 :(得分:0)

感谢FrankerZ的正确答案! 所以我的litle示例应该是这样的:

class Program
{
    static void Main(string[] args)
    {
        var task = test();
        task.Wait(); //this stops async behaviour
        int result = task.Result;// get return value form method "test"
        Console.WriteLine("RESULT IS = " + result);
    }

    public async static Task<int> test()
    {
        int a1, a2, a3, a4;

        //run all tasks
        //all tasks are doing their job "at the same time"
        var taskA1 = GetNumber1();
        var taskA2 = GetNumber2();
        var taskA3 = GetNumber3();

        //wait for task results
        //here I am collecting results from all tasks
        a1 = await taskA1;
        a2 = await taskA2;
        a3 = await taskA3;

        //get value from all task results
        a4 = a1 + a2 + a3;
        return a4;
    }

    public static async Task<int> GetNumber1()
    {
        await Task.Run(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("GetNumber1");
                    System.Threading.Thread.Sleep(100);
                }
            });
        return 1;
    }

//其他方法被省略,因为它们与GetNumber1相同     }

答案 3 :(得分:0)

要在方法中使用await关键字,必须为async。这是一个设计决策,可以更容易向后兼容。

Main不能是async,因为它是程序的起点,因此您通常需要以某种方式阻止程序运行。您可以通过阻止Task或甚至通过调用Console.ReadLine来实现此目的。

任务通常在后台线程上执行,如果所有前台线程都停止,后台线程不会使程序保持运行。

我在async-await here

上有一篇介绍性的博客文章

答案 4 :(得分:0)

如果在其中使用await关键字,则无法使方法ReserveAHolliday成为同步。

可能的做法是简单地阻塞,直到两个异步方法返回它们的值。我不推荐它,因为它可能导致死锁或其他意外错误。

如果您没有同步上下文(例如控制台应用程序),您只需编写

即可
bool hotelOK = ReserveAHotel().Result;

但我估计你最终想要创建一个GUI。这就是阻塞分崩离析的地方。因为UI应用程序(Forms和WPF)确实有上下文并且等待异步方法导致死锁。 Read more here

此问题有解决方法,但它们高度依赖于应用程序的设计。

编辑:

如果您的库代码需要同步上下文,那么阻塞肯定会导致死锁。

如果您不需要它,可以在您的库代码中使用ConfigureAwait(false)并远离阻止。

我建议:async / await一直或者没有async / await。还有其他可能使你的代码异步,例如:基于事件,但就像生活中的一切都有利有弊。

请阅读Stephen Cleary的this blog。他解释了为什么阻止异步代码会死锁以及如何避免它。

答案 5 :(得分:0)

  

首先永远不要使用结果,它的实现方式不正确,您   最终将陷入僵局。而是使用 GetAwaiter()。GetResult()

如果您使用的是C#7.0或更低版本,则可以安全地使用此模式:

    static void Main(string[] args)
    {
        MainAsync(args).GetAwaiter().GetResult();
    }

    private static async Task MainAsync(string[] args)
    {
        await MyMethod();
        await MyOtherMethod();
        Console.WriteLine("Done!");
    }

    private static async Task MyMethod()
    {
        // do something
        await Task.CompletedTask;
    }

    private static async Task MyOtherMethod()
    {
        // do something
        await Task.CompletedTask;
    }

如果您使用的是C#7.1+,则可以将main方法更改为异步方法,如下所示:

    static async Task Main(string[] args)
    {
        await MyMethod();
        await MyOtherMethod();
        Console.WriteLine("Done!");
    }

    private static async Task MyMethod()
    {
        // do something
        await Task.CompletedTask;
    }

    private static async Task MyOtherMethod()
    {
        // do something
        await Task.CompletedTask;
    }

在.Net核心项目中,您可以使用以下标记更改为C#7.1或更高版本:

<LangVersion>7.1</LangVersion>

<LangVersion>latest</LangVersion>

项目文件将如下所示:

<Project Sdk="Microsoft.NET.Sdk">    
 <PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>netcoreapp2.1</TargetFramework>
  <LangVersion>latest</LangVersion>
 </PropertyGroup>   
</Project>