2011-09-11 55 views
1

我試圖讓我的一個程序變成多線程應用程序,但我已經打了一個對,我在下面的代碼記錄碰壁的。任何幫助,我可以得到這個,使其行爲正常將不勝感激,所以我可以擴展這個存根到我現有的應用程序的更有效的版本。這個多線程VB.NET(2010 Express Edition)程序爲什麼不能正常工作?

謝謝您對此事有任何意見。 - 亞倫

Imports System.Threading 

Public Class frmMain 

    ''' <summary>Initializes the multithreaded form</summary> 

    Private Sub Initialize() Handles MyBase.Load 

     AddThread(AddressOf Update_UI) 

     running = True 

     For Each Thread In ThreadPool 

      Thread.IsBackground = True 

      Thread.Start() 

     Next 

    End Sub 



    ''' <summary>Terminates the multithreaded form</summary> 

    Protected Overrides Sub Finalize() Handles MyBase.FormClosing 

     running = False 

     For Each Thread In ThreadPool 

      Thread.Join() 

      Thread = Nothing 

     Next 

    End Sub 



    ''' <summary>Adds a worker thread to the ThreadPool</summary> 

    ''' <param name="pointer">The AddressOf the function to run on a new thread.</param> 

    Private Sub AddThread(ByRef pointer As System.Threading.ParameterizedThreadStart) 

     Dim newthread As Integer 

     If ThreadPool Is Nothing Then newthread = 0 Else newthread = ThreadPool.GetUpperBound(0) + 1 

     ReDim Preserve ThreadPool(newthread) 

     ThreadPool(newthread) = New Thread(pointer) 

    End Sub 



    ''' <summary>Updates the User Interface</summary> 

    Private Sub Update_UI() 

     'HELP: The commented out lines in this subroutine make the program work incorrectly when uncommented. 

     'HELP: It should echo 'output' to the titlebar of frmMain, but it also makes the form unresponsive. 
     'HELP: When I force the form to quit, the 'termination alert' does not trigger, instead the application hangs completely on Thread.Join (see above). 
     'HELP: If I remove DoEvents(), the form is unable to be closed...it simply goes unresponsive. Shouldn't the multithreading keep us from needing DoEvents()? 



     'If Me.InvokeRequired Then 

     ' Me.Invoke(New MethodInvoker(AddressOf Update_UI)) 

     'Else 

      While running 
       Dim output As String = System.DateTime.Now + " :: Update_UI() is active!" 

       Debug.Print(output) 
       'Application.DoEvents() 

       'Me.Text = output 

      End While 

      Debug.Print(System.DateTime.Now + " :: Termination signal recieved...") 

     'End If 

    End Sub 

    Delegate Sub dlgUpdate_UI() 



    Private ThreadPool() As Thread 

    Private running As Boolean 

End Class 

回答

1

這是do while循環即燃了所有你的週期,這樣你就失去了戰鬥,並保持忙碌。處理器無論你用多少個線程。類似下面的內容將更適合您嘗試實現的目標。

Imports System.Threading 

Public Class Form1 

    Private t As New Timer(AddressOf DoTimer, Nothing, 1000, 1000) 

    Private Sub DoTimer(ByVal state As Object) 
     UpdateUi() 
    End Sub 

    ''' <summary>Updates the User Interface</summary> 
    Private Sub UpdateUi() 
     If InvokeRequired Then 
      Invoke(New DlgUpdateUi(AddressOf UpdateUi)) 
     Else 
      Dim output As String = DateTime.Now & " :: Update_UI() is active!" 
      Debug.Print(output) 
      Text = output 
     End If 
    End Sub 

    Delegate Sub DlgUpdateUi() 

    Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing 
     t.Dispose() 
    End Sub 
End Class 
2

是的,你試過的都不能正常工作。您已正確識別需要使用Control.Invoke()在主線程上運行Me.Text分配。這是什麼錯誤:

  • 你的invoke()調用使得主線程上運行的整個方法。它將開始執行循環並且永不退出。你的表單會緊張,因爲它不能做任何事情了,像重繪標題欄來顯示更改後的文本或響應用戶輸入
  • 的調用的DoEvents使形式活着回來,但現在你已經有了一個新的問題:用戶可以關閉窗口,並且代碼保持運行。 運行標誌將永遠不會設置爲false,因此程序不會停止。用戶界面不見了。代碼通常會彈上的ObjectDisposedException但不是在您的特定情況下,Text屬性存儲在私有變量
  • 使得只有Me.Text分配在主線程上運行,你可以修改密碼。但是現在你遇到了一個新的問題:主線程會被調用請求打斷,並且不再執行常規(低優先級)任務。它緊張。基本問題是,您嘗試更新標題欄的方式太快。沒有意義,用戶無法快速閱讀。更新20次每秒充足,看起來光滑人眼
  • 不要使用finalize()方法用於這樣的任務,該代碼可以很容易觸發2秒鐘的終結器線程超時,轟炸你的程序。

請考慮使用BackgroundWorker類,它會處理其中的一些細節。

+0

我嘗試過爲此使用BackgroundWorker,但它似乎有使用線程相同的問題......但增加了不知道如何在此上下文中使用它。 –

+0

但至少通過對此的分析,我可以更好地理解我在做什麼錯了我當前使用的線程......感謝您提供有關使用計時器執行更新分配以及拉取我的kill代碼的提示從Finalize()...中刪除,這不會影響這個特定的程序,但它會在我計劃的更復雜的程序中。 –

+0

你會如何建議處理Finalize例程?我希望它在FormClosing上運行,但不會受到潛在的超時和崩潰(所以我可以在更復雜的表單上使用此模板)。換句話說......有沒有辦法阻止表單閉包等待Finalize完成? –

0

如果一旦我說一萬次我已經說過。使用Invoke雖然在許多情況下很有用,但卻被濫用和濫用。如果你想要做的就是將工作線程的進度顯示給用戶,那麼使用Invoke並不總是最好的選擇。在這裏也不是最好的選擇。

而是將您正在分配給output的狀態文本發佈到可通過UI線程訪問的變量中。然後使用System.Windows.Forms.Timer定期以更合理的速度輪詢其價值......也許每隔1秒左右。 Tick事件已經在UI線程上運行,因此您可以立即開始使用此值通過操縱各種UI控件向最終用戶顯示。

字符串真的很容易從線程傳遞到線程,因爲它們是不可變的,這意味着它們本質上是線程安全的。你唯一需要擔心的是確保UI線程看到發佈到共享變量的最新引用。在C#中,您可以使用volatile關鍵字。在VB中,您可以使用UI線程的Thread.VolatileRead和工作線程的Thread.VolatileWrite。當然,如果你更舒適地包裝讀取和寫入SyncLock,這也是完全可以接受的。

相關問題