2015-05-26 61 views
5

有人能澄清这个例子中,这当然是不工作:C#异步方法调用一路主

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; 
    } 

我想“收”通过使用来自GenNumberX方法值“等待”。我想使方法“测试”不是异步的。我不明白为什么测试必须是异步的,当我使用等待获得价值。这个例子让我在每一个使用异步的方法上编写异步,当我钻到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机制的用法吗?

+1

在某些时候,您需要从同步方法中启动一个asynctask。如果你想开火,只需调用它(也称为异步空白,开火并忘记),或者使用'test()。Wait()'来同步等待任务。 – FrankerZ

回答

2

下面是一个完整的例子。你可以同时运行ReserverAHoliday(bool r = ReserveAHolliday()。Result;)和Asynchronously(只需调用ReserveAHolliday();)MAIN(取决于你评论哪一行)。您可以看到效果(预订完成之前/之后会打印“END”)。 我更喜欢await Task.WhenAll()方法,它更具可读性。 还指出,它最好使用Wait.Term.Delay(100),而不是GetNumber1内的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

谢谢盖伊。这个例子能否造成死锁,就像上面答案中提到的@Kabbalah一样? 我应该保持这些方法异步,因为我要编写一个用于WinForms项目的库。这个代码只是一个例子。我实际上正在编写SignalR客户端/包装器,以实现各种软件(ERP-> POS,POS-> ERP)之间的通信,例如:_IsUserOnline(string userId)_,_GetOnlineUsers_等。 – vpetrovic

+0

在WinForms中,可能...取决于在你的图书馆发生了什么。你的库代码是否需要一个同步上下文?如果答案是肯定的,那肯定会陷入僵局。如果答案是否定的,如果您在库代码中指定ConfigureAwait(false),则可能会失去此效果。我会用更多信息编辑我的答案。 – Kabbalah

+0

我建议花时间在此,以免烧伤。 我认为如果你使用await,你更安全,因为你处于异步上下文中,并且最初称为异步方法的同步代码可以继续它的工作... 可以肯定地暗示我建议增加一个长延迟在你的异步内部,所以你可以看到自己的行为影响。另外,我更喜欢从一个bool属性(propfull)启动这样的异步,我将其用作一个标志(这样,当我不需要它时,同一个任务不会并行启动多次)。我不知道这是否适用于你,如果这是很好的做法。 – Guy

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的工作原理,并且可以在Async的美妙世界中启动你。

+0

_ReserveAHolliday_将不工作,因为使用_await_而不声明方法异步。所以看来我必须使用_Task.Wait()_方法。 – vpetrovic

+0

我已经更新了示例。 ReserveAHoliday应该是一个异步任务。就像我刚才提到的那样,我强烈建议您查看我链接的视频。这需要一些时间,但是非常值得。 – FrankerZ

0

谢谢FrankerZ的正确答案! 所以我的痘痘的例子应该是这个样子:

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 }

+1

'Task.Result'阻塞,直到任务完成,所以你不需要'task.Wait()' –

+0

要小心,因为它只能在控制台应用程序中工作。如果您在WPF或Forms应用程序中尝试此操作,它很可能会导致死锁。请参阅我的帖子中有关此问题的链接。 – Kabbalah

+0

不要使用'Thread.Sleep(100)' - 使用'await Task.Delay(100)'代替。 –

0

为了使用一种方法里面await关键字,它具有相同的为async。这是一个设计决定,使其更容易向后兼容。

Main不能是async,因为它是程序的起点,所以您通常需要阻止某种方式来保持程序运行。您可以通过阻止Task或甚至致电Console.ReadLine来完成此操作。

任务通常在后台线程和后台线程上执行,如果所有前台线程停止,后台线程不会让程序继续运行。如果您使用的await关键字它里面

我有一个介绍性的博客文章上async-awaithere

0

方法ReserveAHolliday不能进行同步。

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

如果没有同步环境(如控制台应用程序),你可以简单地写

bool hotelOK = ReserveAHotel().Result; 

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

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

编辑:

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

如果你不需要它,它可能在你的库代码使用ConfigureAwait(false)与阻塞逃脱。

我推荐的是:异步/等待所有的方式或没有异步/等待。还有其他的可能性让你的代码异步,例如:基于事件,但是像生活中的每件事情都有优点和缺点。

请阅读斯蒂芬·克利this blog。他解释了为什么阻止异步代码会死锁,以及如何避免它。

+0

是的,我正在编写一个库项目,这将在WinForms GUI中使用。如果这可能会造成一些问题,我会保留这些方法_async_,这不是大问题,我只是想按照我的计划设计这些类,但是这个计划可能是错误的。 – vpetrovic