处理"繁重"数据时,WinForms UI响应能力

ram*_*nik 5 data-binding user-interface multithreading backgroundworker winforms

我正在修改Windows窗体以允许在UI保持响应的同时在后台加载数据.数据需要花费大量时间来检索和绑定.理想情况下,我会在后台执行这两项工作,但是我在后台应该做什么样的UI更新(如在主线程外部),这有些含糊不清.在后台显示数据检索和数据绑定的一个可靠示例将非常有用.

STW*_*STW 6

检索可以而且应该被推送到后台线程 - 但是有一些模式可以将它们全部放在适当位置.

基本上你会启动一个后台线程来检索数据,一旦完成它就需要合并回UI线程来进行实际的UI更新(跨线程的UI更新是坏的坏事).

您可以通过三种基本的背景线程方式进行探索

  • 最简单/最有限(和古怪的IMO)是BackgroundWorker组件
  • 使用Delegates及其BeginInvoke()/ EndInvoke()方法提供了简便性和灵活性的平衡(并使用ThreadPool线程)
  • 使用原始Thread对象提供最多的控件,但设置比ThreadPool Threads慢

我个人倾向于代表选项; 一旦你获得模式,它们就很容易使用.BackgroundWorker在前面看起来不错,但有一些问题和缺少的管道,使得它比你期望的更麻烦.让我简单介绍一下代表方法的样本; 我马上就会更新......

编辑

这里有一些代码,它在VB中,但如果你是C#家伙,应该很容易转录.关于你希望后台线程的行为方式你还有几个选项,所以这里有两个示例.非阻塞是我的首选,但如果您将其融入现有代码中,那么阻止对您来说可能更容易.

非阻塞,一旦后台线程完成,将在UI线程上调用回调方法(GetData_Complete)

Sub Main()

    Console.WriteLine("On the main thread")
    Dim dataDelegate As New GetDataCaller(AddressOf GetData)

    Dim iar As IAsyncResult

    ' Non-blocking approach using a callback method
    iar = dataDelegate.BeginInvoke(AddressOf GetData_Complete, Nothing)

End Sub

Private Delegate Sub GetData_CompleteCaller(ByVal iar As IAsyncResult)
Private Sub GetData_Complete(ByVal iar As IAsyncResult)
    If InvokeRequired Then
        Dim invokeDelegate As New GetData_CompleteCaller(AddressOf GetData_Complete)
        Invoke(invokeDelegate, New Object() {iar})
        Exit Sub
    End If

    ' Downcast the IAsyncResult to an AsyncResult -- it's safe and provides extra methods
    Dim ar As System.Runtime.Remoting.Messaging.AsyncResult = DirectCast(iar, System.Runtime.Remoting.Messaging.AsyncResult)

    Dim dataDelegate As GetDataCaller = DirectCast(ar.AsyncDelegate, GetDataCaller)
    Dim result As String = dataDelegate.EndInvoke(iar)

    Console.WriteLine("On the main thread again, background result is: " + result)

End Sub

Private Delegate Function GetDataCaller() As String
Private Function GetData() As String
    Console.WriteLine("On the background thread!")

    For index As Integer = 0 To 2
        Console.WriteLine("Background thread is working")
    Next

    Return "Yay, background thread got the data!"

End Function
Run Code Online (Sandbox Code Playgroud)

阻止 子主()

    Console.WriteLine("On the main thread")
    Dim dataDelegate As New GetDataCaller(AddressOf GetData)

    Dim iar As IAsyncResult

    ' blocking approach; WaitOne() will block this thread from proceeding until the background thread is finished
    iar = dataDelegate.BeginInvoke(Nothing, Nothing)
    iar.AsyncWaitHandle.WaitOne()
    Dim result As String = dataDelegate.EndInvoke(iar)
    Console.WriteLine("On the main thread again, background result is: " + result)

End Sub

Private Sub GetData_Complete(ByVal iar As IAsyncResult)

    ' Downcast the IAsyncResult to an AsyncResult -- it's safe and provides extra methods
    Dim ar As System.Runtime.Remoting.Messaging.AsyncResult = DirectCast(iar, System.Runtime.Remoting.Messaging.AsyncResult)

    Dim dataDelegate As GetDataCaller = DirectCast(ar.AsyncDelegate, GetDataCaller)
    Dim result As String = dataDelegate.EndInvoke(iar)

    Console.WriteLine("On the main thread again, background result is: " + result)

End Sub

Private Delegate Function GetDataCaller() As String
Private Function GetData() As String
    Console.WriteLine("On the background thread!")

    For index As Integer = 0 To 2
        Console.WriteLine("Background thread is working")
    Next

    Return "Yay, background thread got the data!"

End Function
Run Code Online (Sandbox Code Playgroud)