线程函数有多个参数并返回数据

发布于 2024-10-11 19:00:12 字数 3393 浏览 4 评论 0 原文

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

我一直在尝试使用 Delegate Sub 来调用该函数(它位于一个单独的类中),并且我的代码还尝试从该函数返回一个数据集,以便调用线程更新 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 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 事件被调用(至少函数没有被调用)。

任何人都可以提供一些关于如何调试代码以了解为什么这些函数没有被调用的帮助吗?

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

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

马特

I am working on a WPF .NET 3.5 application that does a few long tasks that I would like to make a seperate thread to the UI thread to process the data and then when completed update some labels in the UI. The problem I am having is that the function I have uses two parameters and I am struggling to work out how to call a function with multiple parameters in a thread and update the UI.

I have been playing around with using a Delegate Sub to call the function (it is located in a seperate Class), and my code was also attempting to return a dataset from the function for the calling thread to update the UI, but I am not sure if this is the best practice to achieve this or wether I should use a dispatcher for the called function to do the UI updating (feedback would be greatly appreciated).

My code is as follows.

    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

And my function that I am attempting to call is as follows:

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

My problem is if I was to have the calling thread to update the UI, how do I have the called thread to return a dataset, or if the called thread is to update the UI, how do I go about achieving this?

Update:

Following the recomendations provided, I have impletemented a BackgroundWorker that raises a DoWork and RunWorkerCompleted events to get the data and update the UI, respectively. My updated code is as follows:

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

I then have in a seperate class my functions that get and process the data.

I am able to debug the code in Visual Studio 2010 and the form displays but the labels are not updating, and when I put a breakpoint at the RunWorkerAsync line the line is called and the Window_Loaded sub completes but it appears that none of the DoWork or RunWorkerCompleted events are called (well at least the functions are not).

Can anyone provide some assistance on how I can debug the code to see why these functions are not being called?

Also, is the above code the correct method that was recommended in the answers?

Any assistance provided will be greatly appreciated.

Matt

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

坦然微笑 2024-10-18 19:00:12

您应该使用BackgroundWorker 组件

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

You should use the BackgroundWorker component.

You should call your function in the DoWork handler and set e.Result to the returned DataSet.
You can then update the UI in the RunWorkerCompleted handler.

说不完的你爱 2024-10-18 19:00:12

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

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

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

请注意,您还可以在长时间运行的方法完成后使用其结果更新 UI。这以与进度报告类似的方式完成:实现一个类来包含结果,将 DoWorkEventArgsResult 属性设置为此类的实例,然后将结果设置为当引发 RunWorkerCompleted 事件时,将在 WorkCompletedEventArgsResult 属性中提供。

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

Use a BackgroundWorker. Implement your long-running method, and pass the arguments to the method in the DoWorkEventArgs parameters of the DoWork event handler. Do not update the UI, either directly or indirectly (i.e. don't update properties of your view model), in this method.

Use progress reporting to update the UI while the method is running: call ReportProgress in the long-running method, passing any information that needs to appear in the UI in the UserState parameter. In the ProgressChanged event handler, get the state from the ProgressChangedEventArgs and update the UI (by, one hopes, updating the appropriate properties of your view model and raising PropertyChanged).

You'll need to implement a class to contain the user state for progress reporting, since UserState is of type object.

Note that you can also update the UI with the results of the long-running method when it's complete. This is done in a similar fashion to progress reporting: implement a class to contain the results, set the Result property of the DoWorkEventArgs to an instance of this class, and the result will be available in the Result property of the WorkCompletedEventArgs when the RunWorkerCompleted event is raised.

Make sure that you handle any exceptions that the long-running method raises by checking the Error property of the WorkCompletedEventArgs.

不再让梦枯萎 2024-10-18 19:00:12

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

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

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

I don't have much experience with BackgroundWorker (I have only used it once), but it is definitely a solution to your problem. However, the approach I always use is to start a new Thread (not ThreadPool thread via delegates), which acquires a lock and then updates all of the properties. Provided that your class implements INotifyPropertyChanged, you can then use databinding to have the GUI automatically update any time the property changes. I have had very good results with this approach.

As far as passing a Dispatcher to your thread goes, I believe you can do that as well. However, I would tread lightly because I believe I have run into cases with this where the Dispatcher I think I'm using is no longer associated with the main thread. I have a library that needs to call a method that touches GUI elements (even though the dialog might not be displayed), and I solved this problem by using Dispatcher.Invoke. I was able to guarantee that I was using the Dispatcher associated with the main thread because my application uses MEF to Export it.

If you'd like more details about anything I've posted, please comment and I'll do my best to embellish on the topics.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文