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

问题描述:

我正在做一个WPF .NET 3.5应用程序,它执行几个长任务,我想使UI线程分离线程来处理数据,然后在完成更新时UI中的标签。我遇到的问题是我使用两个参数的功能,我正在努力解决如何在一个线程中调用带有多个参数的函数并更新UI。Threaded Function有多个参数并返回数据

我一直在玩弄使用Delegate Sub来调用函数(它位于一个单独的类),我的代码也试图从调用线程的函数返回一个数据集来更新UI,但我不确定这是否是实现此目标的最佳实践,或者我应该使用调度程序来调用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,我该如何去做到这一点?

更新:

按照提供的recomendations,我impletemented这引起了DoWork的和RunWorkerCompleted事件来获取数据,并更新了UI,分别一个BackgroundWorker。我更新的代码如下:

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事件被调用(至少该函数不是)。

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

另外,上述代码是在答案中推荐的正确方法吗?

提供的任何援助将不胜感激。

马特

+0

我已经更新了这个问题,我已经按照推荐实现了BackgroundWorker,但是我遇到了两个函数来处理DoWork和RunWorkerCompleted事件的问题。 – Lima 2011-01-13 11:47:35

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

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

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

请注意,您也可以在完成时使用长时间运行方法的结果更新UI。这是以类似于进度报告的方式完成的:执行一个类来包含结果,将DoWorkEventArgsResult属性设置为该类的一个实例,当RunWorkerCompleted事件发生时,结果将在WorkCompletedEventArgsResult属性中可用被提出。

确保您通过检查WorkCompletedEventArgsError属性来处理长时间运行的方法引发的任何异常。

您应该使用BackgroundWorker component

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

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

只要将调度程序传递给你的线程,我相信你也可以做到这一点。不过,我会轻率地行事,因为我相信我遇到过这种情况,我认为我使用的调度程序不再与主线程相关联。我有一个库需要调用一个触及GUI元素的方法(即使对话框可能不显示),我使用Dispatcher.Invoke解决了这个问题。我能够保证我使用与主线程相关联的Dispatcher,因为我的应用程序使用MEF导出它。

如果您想了解更多关于我发布的任何内容的详细信息,请发表评论,我会尽我所能对这些主题进行修饰。

+0

这将无法正常工作;您将从bckground线程更新UI对象。另外,你应该使用ThreadPool。 – SLaks 2011-01-12 17:17:09

+0

当您使用“BackgroundWorker”的进度报告功能时,您可以更新UI对象。很少有必要在WPF应用程序中显式使用`System.Threading`。 – 2011-01-12 18:06:50