【问题标题】:VB.Net + WebService: main form unresponsive on loadVB.Net + WebService:加载时主窗体无响应
【发布时间】:2014-10-20 15:05:17
【问题描述】:

我有一个使用 Web 服务 (SOAP) 的带有 link to sql 的小型 VB.Net 项目。 无论如何,我必须确保所有表单都完全响应,并且运行良好。我唯一的问题是加载应用程序! 主启动窗体只有一行代码:

Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Await objWebService.GetCurrentSessionsAsync
End Sub

但是当这个“等待”代码正在执行时,表单没有响应、冻结并显示等待光标。

对可能导致此问题的原因以及如何处理有任何想法吗?

【问题讨论】:

  • 检查我的编辑,我在 PanagiotisKanavos 的评论后修改了我的答案。

标签: vb.net web-services async-await


【解决方案1】:

关于你的答案,如果你不结合不同的编程模式,代码会更简洁,看看这个:

Private Async Sub frmMain_Load(sender As Object,
                               e As EventArgs) Handles MyBase.Load
  Dim res = Await GetCurrentSessionsAsync()
End Sub

Private Async Function GetCurrentSessionsAsync() As Task(Of com.services.Server)
  Try
    Return Await Task.Factory.
      StartNew(Function() objWebService.GetCurrentSessions)
  Catch ex As Exception
    Glob.ErrorLog("GetCurrentSessions", ex, True)
    Return New com.services.Server
  End Try
End Function

参考资料:

【讨论】:

  • 太棒了!谢谢,我有一种感觉,我可以用更简单的方式实现这一点,但由于我还不是这方面的专家,我担心我永远不会得到它: $
【解决方案2】:

关键问题是Async 不会神奇地使您的方法异步。它只让编译器知道您的方法将包含Await 关键字,并且代码需要为converted into a state machine任何未被等待的代码都会同步执行,即使方法被标记为Async。考虑以下示例:

Private Async Sub Form1_Load(sender As Object,
                             e As EventArgs) Handles MyBase.Load
  Await LongRunning1() 'opens the form, then asynchronously changes
                       'Text property after 2 seconds
End Sub

Private Async Function LongRunning1() As Task
  Await Task.Factory.StartNew(Sub() Threading.Thread.Sleep(2000))
  Me.Text = "Finished LongRunning1"
End Function

这里一个长时间运行的进程,以Thread.Sleep为例,被包装到一个Task中,并且有一个Await关键字。它告诉编译器在执行下一行之前等待任务中的语句完成。如果没有AwaitText 属性将立即设置。

现在假设您的 Async 方法中有一些长时间运行的同步代码:

Private Async Sub Form1_Load(sender As Object,
                             e As EventArgs) Handles MyBase.Load
  Await LongRunning2() 'synchronously waits 2 seconds, opens the form,
                       'then asynchronously changes Text property after 2 seconds
End Sub

Private Async Function LongRunning2() As Task
  Threading.Thread.Sleep(2000)
  Await LongRunning1()
  Me.Text = "Finished LongRunning2"
End Function

请注意,在这种情况下,它会同步等待 Thread.Sleep 完成,因此对于最终用户,您的应用显示为挂起。底线是 - 必须知道哪些方法调用可以长时间运行,并将它们包装到基于任务的等待模型中。否则你可能会看到你所看到的问题。

如果这听起来太复杂,您可以启动后台工作程序 (.NET 2.0+),或使用 TPL (.NET 4.0+) 启动任务。如果您希望进入较低级别,则从 .NET 1.1 开始可以使用线程。然后在表单/控件的顶部显示一些等待/进度窗口/覆盖,该功能尚不可用。看看这些:

感谢@PanagiotisKanavos 为我指明了正确的方向。

【讨论】:

  • 这是不正确的。您可以拥有异步事件处理程序,因此存在async voidAsync Sub 语法。将此语法用于事件处理程序以外的任何内容实际上是一个错误。
  • @PanagiotisKanavos:你是对的。我对此进行了更多研究,并编辑了我的答案。让我知道你的想法。
  • 我注意到太晚了,我标记了你的答案!谢谢你的更新。
  • 猜猜看,我的问题只是第一次调用 objWebService!如果我在第一个之后再打电话,表格会保持响应!我将提供我稍后制作的解决方案,以便您查看;)
  • @Saleem:Web 服务通常是一个薄层,它只是将对象打包成一种安全的形式,因此理想情况下,第一次调用不会花费很长时间。无论如何,WCF 是旧的,您可能需要考虑 Web API。 reddit.com/r/csharp/comments/17oyq6/… 它似乎也有第一次通话问题,但是 - stackoverflow.com/questions/26160950/…,但这些可能无关。
【解决方案3】:

所以这里是什么(我不得不说 Neolisk 和 Panagiotis 的回答让我找到了解决方案): 使我的加载表单无响应的原因似乎是 Web 服务中的一个错误,只有第一次调用我的 Web 服务才会产生这个问题。因此,如果在表单加载后进行第一次调用,在另一个事件中,我将面临同样的问题。 为了解决这个问题,我改变了使用TaskCompletionSource 变量通过Web 服务调用我的第一个方法的方式,并使用Thread 调用我的第一个方法。我将发布我之前/之后的代码,以确保我清楚地交付了我的修复。

之前:

Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim res = Await objWebService.GetCurrentSessionsAsync
End Sub

之后:

Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim res = Await GetCurrentSessionsAsync()
End Sub

Dim _tcServer As New TaskCompletionSource(Of MyProject.com.services.Server)

Private Async Function GetCurrentSessionsAsync() As Task(Of com.services.Server)
    Try
        Dim th As New System.Threading.Thread(AddressOf GetCurrentSessions)
        th.Start()
        Return Await _tcServer.Task
    Catch ex As Exception
        Return New MyProject.com.services.Server
    End Try
End Function

Private Sub GetCurrentSessions()
    Try
        Dim res = objWebService.GetCurrentSessions
        _tcServer.SetResult(res)
    Catch ex As Exception
        Glob.ErrorLog("GetCurrentSessions", ex, True)
    End Try
End Sub

我希望这可以在将来对其他人有所帮助。 谢谢。

【讨论】:

  • 查看我的其他答案(我决定将其分开,以避免混淆)。
猜你喜欢
  • 1970-01-01
  • 2015-12-01
  • 2019-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-15
相关资源
最近更新 更多