2017-06-16 134 views
0

我一直在研究一个项目,同时学习vb.net:一个多线程代理检查器。我有它的工作,并在小测试(列表1000代理检查)它工作得很好。但是,我想用它来检查500,000或更多的代理列表。当我尝试这样做时,我看到了大量的CPU使用情况。我有一个带有16GB内存的AMD FX-8320,仅供参考。需要帮助减少多线程VB.net程序的CPU使用率

我的所有代码都可以在我的Github上查看(click this to visit),但我会在这里复制主要的重要部分。

基本流程:

  1. 用户点击“开始”,并且每个线程启动上 “threadedProxyChecker()”
  2. threadedProxyChecker()通过列表(串)的所有成员进行迭代包含所有代理从文本文件加载
  3. 由每个线程正在测试的代理被加载到一个临时List(Of String)中,所以工作不会进行两次,并且此List(Of String)受SyncLock保护。调用“checkProxy(proxy)”,然后将代理从临时List(Of String)中删除。
  4. 结果记录到l1工作或l2失败。 (可能不需要l2,只是一个存储在int中的所有失败数的计数?)
  5. “performStep()”更新UI以显示ListBox中的工作代理,增加ProgressBar并报告完成百分比作为标签中工作/不响应的计数。
  6. 当每个线程到达列表末尾时,工作/无响应代理的总数与列表大小进行比较,作为程序结束的条件。

    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 
    
    :所有工作完成后(我知道这是不好的,但我不知道我还能怎么做)

我如何检查每个代理Thread.Abort的()被调用

每个线程执行的主要代码:

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 

的“performStep()”方法,通过“threadedProxyChecker()”之称

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占用率将真棒!谢谢:) -Eric

+1

永远不要调用'Thread.CurrentThread.Abort()' - 只有**异常是因为如果你试图崩溃你的程序并且你想要结束所有的线程。调用'.Abort()'可能会破坏.NET运行时状态,并且不能依赖它在之后正确运行。 – Enigmativity

+0

还有一个很好的机会,就是你用所有的'.Invoke'调用来杀死你的CPU。你想尽可能避免它们。您应该从UI线程中整理数据,并在后台线程上整理进程,然后将数据编组回到UI线程一次。 – Enigmativity

+0

@Enigmativity然后我应该如何终止我的线程?每当他们到达该方法的结尾时,它会自动终止吗? –

回答

1

执行重复性任务的线程应该在Sleep的某个地方在他们的循环的“yield”处理时间到其他线程。

在您的循环中的Next语句之前放置一个Sleep(1)语句。

0

线程有两种主要用途。

  1. 不要在后台工作,以保持响应用户界面
  2. 做平行

更多的工作,你的代码来看,我会说,你的目标为(2),这意味着你会增加CPU负载(这通常是件好事,闲置的CPU会被浪费)。如果你的代码使用了太多的CPU,那么你可以考虑降低可执行文件的优先级。

'Process Priority 
Dim CurrentProcess As Process = Process.GetCurrentProcess 
CurrentProcess.PriorityClass = ProcessPriorityClass.BelowNormal 
'Thread Priority 
Dim CurrentThread As Thread = Thread.CurrentThread 
CurrentThread.Priority = ThreadPriority.BelowNormal 

它仍然会使用相同数量的CPU,但它会更好地屈服于其他进程。

如果你想优化你的代码一点,我建议你有URL列表:■检查,和你的线程做一个指数的SyncLock选择,而不是..喜欢的东西:

Dim ProxyList As New List(Of String) ' The list of URL:s 
Dim Index As Integer = 0 ' The index to use by next thread 
Dim IndexObject As New Object ' The SyncLock object 

' In the thread 
Dim Value As String ' The URL we get 
SyncLock IndexObject 
    If Index >= ProxyList.Count Then Return ' We are at the end, we should bail out 
    Value = ProxyList(Index) ' Get the value 
    Index += 1 ' Increment the counter 
End SyncLock 

就像现在这样,每个线程都会执行大量的SyncLock和查找操作,这可能会减少CPU占用率。

+0

对于主线程来说,向工作线程发放工作并不是更好,每个线程都需要查看特定的代理?然后,您可以完全切断源数据结构上的锁定(避免潜在的争用,我不确定这是否会成为问题,因为我不确定工作通常需要多长时间),而且您只需要在工人完成工作后,担心会合并结果。 – Craig