VB.Net 多个后台工作人员 - 仅最后一个任务完成
我一直在努力让它发挥作用。如果我在调试器中单步执行代码,一切都会很好。
我的问题是,如果我只是运行它,只有最后一个任务响应。我猜我正在覆盖后台工作或其他东西。我确信我做错了一些事情,但我的代码现在很混乱,因为我在搜索时尝试了很多方法。我知道线程池和 .Net 4.0 任务,但很难完成我需要的事情。
基本上我正在编写一个程序(更有可能尝试),该程序获取计算机列表和 ping,然后检查它们的正常运行时间并返回报告。
这在 UI 线程中工作得很好(显然这会锁定我的屏幕)。我可以让后台工作人员执行此操作,但随后它会逐一执行每台计算机,并且虽然屏幕响应良好,但仍然需要很长时间。
所以我的答案是为每个服务器启动一个新的后台工作线程有一个 for 循环。我的解决方案不起作用。
我已经看到其他线程可以做到这一点,但我需要与事件一起使用来调用代码以在每个线程完成时更新 UI。
最简单的方法是什么?
这是我的代码。大多数只是复制粘贴+修改,直到我让它正常工作。
所以在主类中我有测试人员。
(我尝试使用 Testworker() 但它说我无法使用Events 做到这一点)
当我单击按钮时,列表会加载。
Private WithEvents TestWorker As System.ComponentModel.BackgroundWorker
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
Button1.IsEnabled = False
Dim indexMax As Integer
indexMax = DataGridStatus.Items.Count
For index = 1 To (indexMax)
Dim Temp As ServerInfo = DataGridStatus.Items(index - 1)
Temp.Index = index - 1
Call_Thread(Temp)
Next
End Sub
Private Sub Call_Thread(ByVal server As ServerInfo)
Dim localserver As ServerInfo = server
TestWorker = New System.ComponentModel.BackgroundWorker
TestWorker.WorkerReportsProgress = True
TestWorker.WorkerSupportsCancellation = True
TestWorker.RunWorkerAsync(localserver)
End Sub
Private Sub TestWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles TestWorker.DoWork
Dim iparray As IPHostEntry
Dim ip() As IPAddress
Dim Server As ServerInfo
Server = e.Argument
Try
'Get IP Address first
iparray = Dns.GetHostEntry(Server.ServerName)
ip = iparray.AddressList
Server.IPAddress = ip(0).ToString
'Try Pinging
Server.PingResult = PingHost(Server.ServerName)
If Server.PingResult = "Success" Then
'If ping success, get uptime
Server.UpTime = GetUptime(Server.ServerName)
Else
Server.PingResult = "Failed"
End If
Catch ex As Exception
Server.PingResult = "Error"
End Try
TestWorker.ReportProgress(0, Server)
Thread.Sleep(1000)
End Sub
Private Sub TestWorker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles TestWorker.ProgressChanged
Dim index As Integer
Dim serverchange As ServerInfo = DirectCast(e.UserState, ServerInfo)
index = DataGridStatus.Items.IndexOf(serverchange)
' index = serverchange.Index
DataGridStatus.Items.Item(index) = serverchange
' ProgressBar1.Value = e.ProgressPercentage
DataGridStatus.Items.Refresh()
End Sub
I have been pulling my hair out trying to get this to work. If I step through the code in debugger it all works great.
My problem is if I just run it, only the last task responds. I'm guessing I am overwriting the background working or something. I am sure I am doing a few things wrong but my code is now messy as I tried many way while searching. I know of the threadpool and .Net 4.0 tasks but having a hard time getting to do what I need.
Basicly I am writing a program (trying more likely) that takes a list of computers and pings then, then checks their uptime and reports back.
This works fine in the UI thread (Obviously that locks up my screen). I can have the background worker just do this, but then it does each computer 1 by one, and while the screen is responsive it still takes a long time.
So my answer was to have a for loop for each server launching a new background worker thread. My solution does not work.
I have seen other threads that I could do it, but I need to use with events to call code to update to UI when each is done.
What is the most simple way to do this?
Here is my code. Most is just copy paste + modify till I get it working right.
So In the main class I have the testworker.
(I tried using Testworker() but it said I could not do that WithEvents)
When I click the button the list loads.
Private WithEvents TestWorker As System.ComponentModel.BackgroundWorker
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
Button1.IsEnabled = False
Dim indexMax As Integer
indexMax = DataGridStatus.Items.Count
For index = 1 To (indexMax)
Dim Temp As ServerInfo = DataGridStatus.Items(index - 1)
Temp.Index = index - 1
Call_Thread(Temp)
Next
End Sub
Private Sub Call_Thread(ByVal server As ServerInfo)
Dim localserver As ServerInfo = server
TestWorker = New System.ComponentModel.BackgroundWorker
TestWorker.WorkerReportsProgress = True
TestWorker.WorkerSupportsCancellation = True
TestWorker.RunWorkerAsync(localserver)
End Sub
Private Sub TestWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles TestWorker.DoWork
Dim iparray As IPHostEntry
Dim ip() As IPAddress
Dim Server As ServerInfo
Server = e.Argument
Try
'Get IP Address first
iparray = Dns.GetHostEntry(Server.ServerName)
ip = iparray.AddressList
Server.IPAddress = ip(0).ToString
'Try Pinging
Server.PingResult = PingHost(Server.ServerName)
If Server.PingResult = "Success" Then
'If ping success, get uptime
Server.UpTime = GetUptime(Server.ServerName)
Else
Server.PingResult = "Failed"
End If
Catch ex As Exception
Server.PingResult = "Error"
End Try
TestWorker.ReportProgress(0, Server)
Thread.Sleep(1000)
End Sub
Private Sub TestWorker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles TestWorker.ProgressChanged
Dim index As Integer
Dim serverchange As ServerInfo = DirectCast(e.UserState, ServerInfo)
index = DataGridStatus.Items.IndexOf(serverchange)
' index = serverchange.Index
DataGridStatus.Items.Item(index) = serverchange
' ProgressBar1.Value = e.ProgressPercentage
DataGridStatus.Items.Refresh()
End Sub
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您只能得到最后的结果,因为每次调用
TestWorker = New System.ComponentModel.BackgroundWorker
时,都会让您的BackgroundWorker
崩溃。由于处理是异步完成的,因此在前面的工作完成之前,在for
循环中会多次调用此行。像下面这样的东西可能会起作用。 (抱歉,我的 VB 很生疏;可能有更有效的方式来表达这一点。)
You are only getting the last result because you are blowing away your
BackgroundWorker
each time you callTestWorker = New System.ComponentModel.BackgroundWorker
. Since the processing is being done asynchronously, this line is being called multiple times within yourfor
loop before the previous work has finished.Something like the following might work. (Sorry, my VB is rusty; there are probably more efficient ways of expressing this.)
更新:我发布这个答案的重点是从技术角度来看您正在尝试做的事情(使用许多后台工作人员),而没有真正考虑这是否是完成您的<的好方法。 em>真实目标。事实上,我认为您可以使用单个
BackgroundWorker
和类似Parallel.ForEach
在其DoWork
事件处理程序中循环(这可以处理很多具体工作,例如 戴夫的解决方案)。当您在 VB 中声明
WithEvents TestWorker As BackgroundWorker
时,它会将其包装成类似这样的内容(不完全正确 - 这只是为了说明这个想法):当您意识到这一点时,很明显,通过设置
TestWorker
到一个newBackgroundWorker
在每次调用Call_Thread
时,您将从中删除任何附加的处理程序该字段先前引用的对象。最明显的修复方法就是在每次调用
Call_Thread
时创建一个新的 localBackgroundWorker
对象,并在那里附加处理程序(使用AddHandler
和RemoveHandler
),然后让它做它的事情:只要从 UI 线程中创建工作线程就应该没问题,因为
BackgroundWorker
自动附加到其构造函数中的当前SynchronizationContext
(如果我没记错的话)。Update: I posted this answer focusing on exactly what you were trying to do from a technical standpoint (use many background workers) without really putting much thought into whether or not this was a good way to accomplish your real objective. In fact, I think you could achieve what you're going for much more easily with a single
BackgroundWorker
and something like aParallel.ForEach
loop in itsDoWork
event handler (this takes care of a lot of the nitty gritty work in, e.g., Dave's solution).When you declare
WithEvents TestWorker As BackgroundWorker
in VB it wraps it up something like this (not exactly—this is just to illustrate the idea):When you realize this, it becomes clear that by setting
TestWorker
to a newBackgroundWorker
on every call toCall_Thread
, you are removing any attached handlers from the object previously referenced by the field.The most obvious fix would simply be to create a new local
BackgroundWorker
object in each call toCall_Thread
, attach the handlers there (usingAddHandler
andRemoveHandler
), and then just let it do its thing:Creating the worker right there in the method should be fine as long as you do so from the UI thread, since
BackgroundWorker
automatically attaches to the currentSynchronizationContext
in its constructor (if I remember correctly).理想情况下,您应该只使用 1 个后台工作程序,并像这样使用它:
Ideally you should use only 1 backgroundworker and use it like this:
您需要将
TestWorker_DoWork
和TestWorker_ProgressChanged
附加到Call_Thread
内的DoWork
和ProgressChanged
事件。我还没有检查其余的代码,但这就是为什么它现在没有做任何事情。You need to attach
TestWorker_DoWork
andTestWorker_ProgressChanged
to theDoWork
andProgressChanged
events withinCall_Thread
. I haven't yet examined the rest of the code, but that is why it isn't doing anything now.