【发布时间】:2017-06-16 15:01:30
【问题描述】:
我在学习 vb.net 时一直在做一个项目:一个多线程代理检查器。我让它工作,并且在小型测试(1000 个要检查的代理列表)上它工作得很好。但是,我想用它来检查 500,000 个或更多代理的列表。当我尝试这样做时,我看到大量的 CPU 使用率。我有一个 16GB 内存的 AMD FX-8320,仅供参考。
我的所有代码都可以在我的 Github (click this to visit) 上查看,但是我将在这里复制主要的重要部分。
基本流程:
- 用户点击“开始”,每个线程开始 “线程代理检查器()”
- threadedProxyChecker() 遍历包含从文本文件加载的所有代理的 List(Of String) 的所有成员
- 每个线程正在测试的代理被加载到一个临时 List(Of String) 中,因此工作不会被完成两次,并且这个 List(Of String) 受 SyncLock 保护。调用“checkProxy(proxy)”,然后从临时 List(Of String) 中删除代理。
- 将结果记录到 l1 表示工作或 l2 表示失败。 (可能不需要 l2,只是存储在 int 中的所有失败的计数?)
- “performStep()”更新 UI 以在 ListBox 中显示工作代理,递增 ProgressBar,并报告完成百分比以及 Label 中的工作/无响应计数。
- 当每个线程到达列表末尾时,将工作/无响应代理的总数与列表大小进行比较,作为程序结束的条件。 Thread.Abort() 在所有工作完成后被调用(我知道这很糟糕,但我不确定我还能怎么做)
我如何检查每个代理:
Function checkProxy(proxy As String) As Boolean
Dim myProxy As WebProxy
Dim Temp As String
Try
myProxy = New WebProxy(proxy)
Dim r As HttpWebRequest = HttpWebRequest.Create("http://azenv.net")
r.UserAgent = "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.2 Safari/537.36"
r.Timeout = 3000
r.Proxy = myProxy
Dim re As HttpWebResponse = r.GetResponse()
Dim rs As Stream = re.GetResponseStream
Using sr As New StreamReader(rs)
Temp = sr.ReadToEnd()
End Using
Dim Text = Temp
rs.Dispose()
rs.Close()
r.Abort()
If Text.Contains("HTTP_HOST = azenv.net") Then
If Text.Contains("REQUEST_TIME =") Then
Return True
End If
Else
Return False
End If
Catch ex As Exception
Return False
End Try
Return False
End Function
每个线程执行的主要代码:
Private Sub threadedProxyChecker()
Dim counter As Integer = 0
For Each proxy As String In proxies
SyncLock curProxLock
If tmpProx.Contains(proxy) Then
GoTo Skip
Else
tmpProx.Add(proxy)
End If
End SyncLock
If Not l2.Contains(proxy) Then
If Not l1.Contains(proxy) Then
If (checkProxy(proxy)) Then
performStep(True, proxy)
l1.Add(proxy)
SyncLock curProxLock
tmpProx.Remove(proxy)
End SyncLock
Else
performStep(False, proxy)
l2.Add(proxy)
SyncLock curProxLock
tmpProx.Remove(proxy)
End SyncLock
End If
End If
End If
Skip:
Next
If proxies.Count() <= (l1.Count() + l2.Count()) Then
If Not isBox Then
SyncLock indexLock
MessageBox.Show("Done checking!" & vbNewLine & l1.Count() & " working proxies")
isBox = True
End SyncLock
Label5.Invoke(Sub()
Label5.Text = "Working: " & l1.Count()
Label5.Update()
End Sub)
Label4.Invoke(Sub()
Label4.Text = "Unresponsive: " & l2.Count()
Label4.Update()
End Sub)
End If
End If
Thread.CurrentThread.Abort()
End Sub
线程是如何启动的:
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
isBox = False
Dim threadCount As Integer = TrackBar1.Value
For int As Integer = 1 To threadCount Step 1
d(int.ToString) = New Thread(AddressOf threadedProxyChecker)
d(int.ToString).IsBackground = True
d(int.ToString).Start()
Next
End Sub
“threadedProxyChecker()”调用的“performStep()”方法
Function performStep(bool As Boolean, proxy As String)
If bool Then
ListBox2.Invoke(Sub()
ListBox2.Items.Add(proxy)
ListBox2.TopIndex = ListBox2.Items.Count - 1
ListBox2.Update()
Label5.Text = "Working: " & l1.Count()
Label5.Update()
End Sub)
Else
Label4.Invoke(Sub()
Label4.Text = "Unresponsive: " & l2.Count()
Label4.Update()
End Sub)
End If
count = count + 1
ProgressBar1.Invoke(Sub()
ProgressBar1.PerformStep()
ProgressBar1.Update()
End Sub)
Label1.Invoke(Sub()
Dim percent As Double = Math.Round((count / proxies.Count() * 100), 2, MidpointRounding.AwayFromZero)
Label1.Text = "Progress: " & count & "/" & proxies.Count() & " checked " & "(" & percent & "%)"
Label1.Update()
End Sub)
Return True
End Function
任何关于如何使工作更顺利和/或如何降低 CPU 使用率的建议都很棒!谢谢 :) -埃里克
【问题讨论】:
-
永远不要调用
Thread.CurrentThread.Abort()- 唯一的例外是如果你试图让你的程序崩溃并且你想结束所有线程。调用.Abort()可能会破坏 .NET 运行时状态,之后您无法依赖它正常运行。 -
还有一个很好的机会是你正在用所有的
.Invoke调用来杀死你的 CPU。你想尽可能地避免它们。您应该将数据从 UI 线程、后台线程上的进程编组,然后将数据编组回 UI 线程一次。 -
@Enigmativity 那么我应该如何终止我的线程呢?到达该方法的末尾时会自动终止吗?
-
我的想法是这样的:pastebin.com/JGR8Gzii
-
是的,它们会在启动方法结束时自动结束。请记住,启动线程的成本很高,因此不要仅仅为少量工作启动线程。
标签: vb.net multithreading