异步等待永远不会结束,但同步会立即运行
async await never ends but sync runs instantly
下面的代码只是检查图像的 url 是否有效。当我如下运行它时,它会立即检查并returns
Function testUrl(ByVal myImage As String) As Boolean
myHttpClient.BaseAddress = New Uri(imageUrl)
myHttpResponse = myHttpClient.GetAsync(myImage).result
success= myHttpResponse.IsSuccessStatusCode
End Function
但是当我使用 async-await 运行它时,我不知道会发生什么。因为我在等待行上设置断点并按 f10,所以我没有任何进展,我不确定是否是因为异步,它运行但如果我的断点在 success=[=11 上,我在下一行看不到任何中断=]行。
网址是有效的 url,第一个代码 returns 立即为真。谁能告诉我发生了什么事?
Async Function testUrl_async(ByVal myImage As String) As Task(Of Boolean)
myHttpClient.BaseAddress = New Uri(imageUrl)
myHttpResponse = Await myHttpClient.GetAsync(myImage)
success= myHttpResponse.IsSuccessStatusCode
End Function
编辑:
当我什至尝试下面的代码时,它什么也没做。但是没有异步等待工作
Async Function testUrl_async() As Task(Of Boolean)
myHttpClient.BaseAddress = New Uri("http://www.logoeps.net/")
myImage = "wp-content/uploads/2013/06/Whosebug_logo.jpg"
Try
myHttpResponse = Await myHttpClient.GetAsync(myImage)
Catch ex As Exception
End Function
End Try
您的第一个示例有效,因为访问 Task
的 Result
将 阻塞 当前线程,直到 Result
被设置。幸运的是 HttpClient.GetAsync()
使用不同的线程(或者更可能是 I/O 完成端口)来完成它的工作,否则你的应用程序会死锁。由于这个原因,阻止异步方法通常不是一个好主意。
在第二个示例中更改为 await
意味着当前线程将 不会 阻塞结果。相反,它会在设置结果时将函数的其余部分排队为 运行。此时调用 testUrl_async()
之后的代码将是 运行。如果你只是在此时退出,testUrl_async()
的排队延续将永远没有机会 运行.
听起来您 运行 喜欢我在博客上描述的 common deadlock issue。在您的调用堆栈中,您可能调用了 Task.Wait()
或 Task<T>.Result
。这可能会导致死锁。
当Await
暂停它的方法时,它首先捕获一个"context"。这个 "context" 是 SynchronizationContext.Current
(除非它是 null
,在这种情况下它是 TaskScheduler.Current
)。如果您不熟悉 SynchronizationContext
,这只是意味着如果 运行 在 UI 线程上捕获 UI 上下文,如果 ASP.NET 请求上下文它在 ASP.NET 请求线程或线程池上下文中 运行(除非您使用其他上下文)。此捕获的上下文用于恢复 Async
方法。
稍后,当 awaitable 完成时(在本例中为 HTTP Get),Async
方法会尝试恢复执行。但是,如果调用代码已 阻塞 UI 线程/ASP.NET 请求线程,则该方法无法在该上下文中执行。所以上下文线程被阻塞,等待Async
方法完成,Async
方法被阻塞,等待上下文空闲。死锁。
避免这种僵局的最好方法就是我在 MSDN article on async best practices 中所描述的:
一路使用Async
。换句话说,将对 Task.Wait()
和 Task<T>.Result
的任何调用替换为 Await
.
下面的代码只是检查图像的 url 是否有效。当我如下运行它时,它会立即检查并returns
Function testUrl(ByVal myImage As String) As Boolean
myHttpClient.BaseAddress = New Uri(imageUrl)
myHttpResponse = myHttpClient.GetAsync(myImage).result
success= myHttpResponse.IsSuccessStatusCode
End Function
但是当我使用 async-await 运行它时,我不知道会发生什么。因为我在等待行上设置断点并按 f10,所以我没有任何进展,我不确定是否是因为异步,它运行但如果我的断点在 success=[=11 上,我在下一行看不到任何中断=]行。
网址是有效的 url,第一个代码 returns 立即为真。谁能告诉我发生了什么事?
Async Function testUrl_async(ByVal myImage As String) As Task(Of Boolean)
myHttpClient.BaseAddress = New Uri(imageUrl)
myHttpResponse = Await myHttpClient.GetAsync(myImage)
success= myHttpResponse.IsSuccessStatusCode
End Function
编辑: 当我什至尝试下面的代码时,它什么也没做。但是没有异步等待工作
Async Function testUrl_async() As Task(Of Boolean)
myHttpClient.BaseAddress = New Uri("http://www.logoeps.net/")
myImage = "wp-content/uploads/2013/06/Whosebug_logo.jpg"
Try
myHttpResponse = Await myHttpClient.GetAsync(myImage)
Catch ex As Exception
End Function
End Try
您的第一个示例有效,因为访问 Task
的 Result
将 阻塞 当前线程,直到 Result
被设置。幸运的是 HttpClient.GetAsync()
使用不同的线程(或者更可能是 I/O 完成端口)来完成它的工作,否则你的应用程序会死锁。由于这个原因,阻止异步方法通常不是一个好主意。
在第二个示例中更改为 await
意味着当前线程将 不会 阻塞结果。相反,它会在设置结果时将函数的其余部分排队为 运行。此时调用 testUrl_async()
之后的代码将是 运行。如果你只是在此时退出,testUrl_async()
的排队延续将永远没有机会 运行.
听起来您 运行 喜欢我在博客上描述的 common deadlock issue。在您的调用堆栈中,您可能调用了 Task.Wait()
或 Task<T>.Result
。这可能会导致死锁。
当Await
暂停它的方法时,它首先捕获一个"context"。这个 "context" 是 SynchronizationContext.Current
(除非它是 null
,在这种情况下它是 TaskScheduler.Current
)。如果您不熟悉 SynchronizationContext
,这只是意味着如果 运行 在 UI 线程上捕获 UI 上下文,如果 ASP.NET 请求上下文它在 ASP.NET 请求线程或线程池上下文中 运行(除非您使用其他上下文)。此捕获的上下文用于恢复 Async
方法。
稍后,当 awaitable 完成时(在本例中为 HTTP Get),Async
方法会尝试恢复执行。但是,如果调用代码已 阻塞 UI 线程/ASP.NET 请求线程,则该方法无法在该上下文中执行。所以上下文线程被阻塞,等待Async
方法完成,Async
方法被阻塞,等待上下文空闲。死锁。
避免这种僵局的最好方法就是我在 MSDN article on async best practices 中所描述的:
一路使用Async
。换句话说,将对 Task.Wait()
和 Task<T>.Result
的任何调用替换为 Await
.