2011-09-11 10 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

はい、試したことのどれも正常に動作しません。メインスレッドでMe.Textの割り当てを実行するためにControl.Invoke()を使用する必要性を正しく認識しました。これは間違っています:

  • あなたのInvoke()呼び出しはメソッド全体をメインスレッド上で実行します。ループの実行を開始し、終了しません。変更されたテキストを表示するか、ユーザーの入力に応答するようにキャプションバーを再描画するなど、何もできないのでフォームが激しくなります
  • DoEventsコールはフォームを元に戻しますが、新しい問題が発生しました:ユーザーはウィンドウを閉じてコードを実行し続けることができます。 フラグは、falseに設定されないので、プログラムは停止しません。ユーザーインターフェイスはなくなりました。コードは通常ObjectDisposedExceptionに適用されますが、特定の場合には適用されません。Textプロパティはプライベート変数に格納されます
  • Me.Text割り当てだけがメインスレッド上で実行されるようにコードを変更できます。しかし、今では新しい問題が発生しました。メインスレッドは呼び出し要求によってパームされ、通常の(優先度の低い)職務をやり遂げることはもうありません。それは緊張します。本質的な問題は、キャプションバーの方法を速く更新しようとしていることです。ユーザーはそれを読むことができません。 1秒間に20回更新すると人間の目に滑らかに見える
  • このようなタスクにはFinalize()メソッドを使用しないでください。コードは2秒のファイナライザスレッドのタイムアウトを引き起こし、プログラムを爆発させます。

BackgroundWorkerクラスを使用することを検討してください。これらは、これらの詳細の一部を処理します。

+0

これはBackgroundWorkerを使ってみましたが、Threadを使用するのと同じ問題があるようですが、このコンテキストで使用する方法を正確にはわからないこともあります。 –

+0

しかし、少なくともこれを分析して、現在のスレッドの使い方が間違っていることを理解することができます...タイマーを使用して更新割り当てを行い、kill-codeを取得するヒントをありがとうこの特定のプログラムには必ずしも影響しませんが、もっと複雑なプログラムでは計画しています。 –

+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に書いてください。

関連する問題