2016-09-02 83 views
2

我有关于异步作业的执行顺序的问题。回调异步并等待

我会问我的问题与例子,因为它更容易可以理解的。

距离https://msdn.microsoft.com/en-us/library/mt674882.aspx一些扭曲的官方例子。

async Task<int> AccessTheWebAsync() 
{ 
    HttpClient client = new HttpClient(); 

    //async operation: 
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); 

    // You can do work here that doesn't rely on the string from GetStringAsync. 
    DoWork1(); 

    // The await operator suspends AccessTheWebAsync. 
    string urlContents = await getStringTask; 

    DoWork2(); 

    return urlContents.Length; 
} 

我可以说DoWork2client.GetStringAsync回调?

如果是这样,DoWork2client.GetStringAsync完成后不立即执行如果DoWork1的运行时间比client.GetStringAsync更长。

我在这儿吗?

+0

是的,你开始了任务,当'DoWork1()'完成时,你将会等待任务。如果它已经完成,“DoWork2”立即启动。如果不是的话,当任务完成时'DoWork2'开始。 –

+0

@AlexanderDerck然后它不是我预期的那种“回调”。在我看来,一旦异步函数完成,应立即执行回调。 – derek

+1

只是为了澄清:即使DoWork1尚未完成,您还是希望DoWork2能够执行(完成getStringTask)吗?也许看看[Task.ContinueWith](https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.continuewith.aspx)方法。 –

回答

5

DoWork2不立即执行完成 client.GetStringAsync如果DoWork1运行时间长于 client.GetStringAsync

一旦你打的点await client.GetStrinkAsync以下,DoWork1()已经完成,从你的例子看起来那么它的执行是同步的。一旦client.GetStringAsync完成,则DoWork2被设置为执行。

的执行流程为:

  1. GetStringAsync异步启动。它会被执行,直到它遇到它的第一个内部await,然后控制回到AccessTheWebAsync
  2. DoWork1()开始。因为它是同步的,所有人都等待它完成
  3. client.GetStringAsync异步等待完成。 await自然会对执行者的调用者产生控制权。
  4. 一旦client.GetStringAsync完成后,执行将返回AccessTheWebAsyncDoWork2()将同步执行。
  5. urlContents.length将被退回。

通常,第一个await关键字之后的所有内容都被设置为该方法其余部分的延续。

+0

有没有办法让'DoWork2'立即执行一次客户端。 GetStringAsync'完成了吗? – derek

+0

@derek但是,这正是发生的事情。一旦'client.GetStringAsync'完成,'DoWork2'将会执行。 –

+0

说'DoWork1()'需要10秒才能完成,而'client.GetStringAsync'需要5秒。所以当'client.GetStringAsync'完成时,'DoWork1()'仍然在运行,所以我们需要等待'DoWork1()'完成,对吗?然后5秒钟后,'DoWork1()'完成。只有在这个时候,'DoWork2()'才能开始工作。纠正我,如果我错了。 – derek

-1

在:

//async operation: 
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); 

要创建一个运行异步操作,并立即返回给调用者任务,下一DoWork1()执行所同步,一旦做​​暂停方法的延续,直到等待任务完成;一旦完成程序继续DoWork2()

+1

说'getStringTask'块与正确“等待”异步方法时发生的情况完全相反。 –

+1

同意:'暂停'或'暂停执行'或将AccessTheWebAsync()的其余部分指定为'getStringTask'的延续是更正确的用法。块不正确(更新) – Ahmad

+1

不,该任务确实立即运行。所有'await'所做的就是接受一个任务(或其他等待),其他代码片段已经启动,并且不执行任何操作(如果任务已经完成),或者暂停执行当前方法直到任务完成。但是,只是为了强调,“等待”不会启动任何事情。 –

4

Yuval's answer是正确的。

要回答你的后续问题:

有没有一种方法,使后立即client.GetStringAsync执行DoWork2完了?

可能。这取决于这个代码的上下文。无法同时在UI线程上运行DoWork1DoWork2(显然,单个线程一次只能执行一件事)。但是,如果这是从线程池上下文中调用的,那么当然,可以同时运行它们。

但是,您应该将思路从“回调”转换为“操作”。也就是说,不要认为DoWork2GetStringAsync的“回调”。相反,可以这样想:我有一个操作GetStringAsync,我有一个操作DoWork2;我需要一个新的操作来完成另一个操作。

一旦你改变你这样的想法,解决的办法是写新的操作方法:

async Task<string> GetStringAndDoWork2Async() 
{ 
    HttpClient client = new HttpClient(); 
    var result = await client.GetStringAsync("http://msdn.microsoft.com"); 
    DoWork2(); 
    return result; 
} 

现在你可以使用你的更高级别的方法,新的操作:

async Task<int> AccessTheWebAsync() 
{ 
    var task = GetStringAndDoWork2Async(); 

    DoWork1(); 

    string urlContents = await task; 

    return urlContents.Length; 
}