2017-06-16 80 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