Threaded Function具有多个参数并返回数据

时间:2011-01-12 13:58:38

标签: wpf multithreading .net-3.5 dispatcher

我正在开发一个WPF .NET 3.5应用程序,该应用程序执行一些长任务,我想创建一个单独的线程到UI线程来处理数据,然后在完成时更新UI中的一些标签。我遇到的问题是我使用的函数使用了两个参数,我正在努力解决如何在线程中调用具有多个参数的函数并更新UI。

我一直在玩使用Delegate Sub来调用该函数(它位于一个单独的Class中),我的代码也试图从函数中返回一个数据集,用于调用线程来更新UI,但是我不确定这是否是达到此目的的最佳实践,或者我应该使用调度函数来执行UI更新(反馈将非常感激)。

我的代码如下。

    Private Delegate Sub WorkHandler(ByVal input1 As String, ByVal input2 As String)
    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
      Dim test_helper As New test_global
      Dim worker As New WorkHandler(AddressOf test_helper.getWeatherData)
      worker.BeginInvoke("IDA00005.dat", "Adelaide", AddressOf weatherCallBack, Nothing)

      ' The following is what I was using prior to attempting to work with threads, do I continue to update the UI here getting the called function to return a dataset, or do I have the called function do the UI updating?
      'Dim ls As DataSet = test_helper.getWeatherData("IDA00005.dat", "Adelaide")
      'Dim f_date As String = ls.Tables("weather").Rows(1).Item(3).ToString
    End Sub
    Public Sub weatherCallBack(ByVal ia As IAsyncResult)
        CType(CType(ia, Runtime.Remoting.Messaging.AsyncResult).AsyncDelegate, WorkHandler).EndInvoke(ia)
    End Sub

我试图打电话的功能如下:

Class test_global
  Public Sub getWeatherData(ByVal filename As String, ByVal location As String) 'As DataSet
    ...
  End Sub
End Class

我的问题是,如果我要调用线程来更新UI,如何让被调用的线程返回数据集,或者如果调用的线程要更新UI,我该如何实现这一点?

更新:

在提供的建议之后,我实现了一个BackgroundWorker,它分别引发DoWork和RunWorkerCompleted事件以获取数据并更新UI。我的更新代码如下:

Class Weather_test
  Implements INotifyPropertyChanged
  Private WithEvents worker As System.ComponentModel.BackgroundWorker
  Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
  Private Sub NotifyPropertyChanged(ByVal info As String)
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
  End Sub
  Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
    Dim test_helper As New test_global
    Dim worker = New System.ComponentModel.BackgroundWorker
    worker.WorkerReportsProgress = True
    worker.WorkerSupportsCancellation = True
    Dim str() = New String() {"IDA00005.dat", "Adelaide"}
    Try
      worker.RunWorkerAsync(str)
    Catch ex As Exception
      MsgBox(ex.Message)
    End Try
  End Sub
  Private Sub worker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork
    Dim form_Helpder As New test_global
    Dim ds As DataSet = form_Helpder.getWeatherData(e.Argument(0), e.Argument(1))
    e.Result = ds
  End Sub
  Private Sub worker_Completed(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
    If e.Error IsNot Nothing Then
      MsgBox(e.Error.Message)
    Else
      ...
      NotifyPropertyChanged("lbl_minToday")
      ... 
    End If
  End Sub
End Class

然后我在一个单独的类中获得了获取和处理数据的函数。

我能够在Visual Studio 2010中调试代码并显示表单,但标签没有更新,当我在RunWorkerAsync行放置一个断点时,调用该行并且Window_Loaded子句完成但是看起来没有调用DoWork或RunWorkerCompleted事件(至少函数不是)。

任何人都可以提供一些协助,帮助我调试代码,看看为什么不调用这些函数?

此外,上述代码是否是答案中推荐的正确方法?

非常感谢所提供的任何帮助。

马特

3 个答案:

答案 0 :(得分:2)

您应该使用BackgroundWorker component

您应该在DoWork处理程序中调用您的函数,并将e.Result设置为返回的DataSet。
然后,您可以在RunWorkerCompleted处理程序中更新UI。

答案 1 :(得分:1)

我对BackgroundWorker没有多少经验(我只使用过一次),但它绝对是您问题的解决方案。但是,我总是使用的方法是启动一个新的Thread(而不是通过委托的ThreadPool线程),它获取一个锁,然后更新所有属性。如果您的类实现了INotifyPropertyChanged,那么您可以使用数据绑定让GUI在属性更改时自动更新。我用这种方法取得了很好的效果。

至于将Dispatcher传递给你的帖子,我相信你也可以这样做。但是,我会轻描淡写,因为我相信我遇到过这种情况,我认为我正在使用的Dispatcher不再与主线程相关联。我有一个库需要调用一个触及GUI元素的方法(即使对话框可能没有显示),我通过使用Dispatcher.Invoke解决了这个问题。我能够保证我正在使用与主线程关联的Dispatcher,因为我的应用程序使用MEF来导出它。

如果您想了解我发布的任何内容的更多详情,请发表评论,我会尽力修饰这些主题。

答案 2 :(得分:1)

使用BackgroundWorker。实现长时间运行的方法,并将参数传递给DoWorkEventArgs事件处理程序的DoWork参数中的方法。请勿在此方法中直接或间接更新UI(即不要更新视图模型的属性)。

使用进度报告在方法运行时更新UI:在长时间运行的方法中调用ReportProgress,将需要在UI中显示的任何信息传递到UserState参数中。在ProgressChanged事件处理程序中,从ProgressChangedEventArgs获取状态并更新UI(希望通过更新视图模型的相应属性并引发PropertyChanged)。

您需要实现一个类来包含进度报告的用户状态,因为UserState的类型为object

请注意,您还可以在完成后使用长时间运行方法的结果更新UI。这与进度报告的方式类似:实现一个类来包含结果,将Result的{​​{1}}属性设置为此类的实例,结果将在{引发DoWorkEventArgs事件时{1}}的{​​1}}属性。

通过检查Result的{​​{1}}属性,确保处理长时间运行方法引发的任何异常。

相关问题