1

自分のプログラムをマルチスレッド アプリケーションにしようとしていますが、次のコードで説明した 2 つの障害にぶつかりました。このスタブを既存のアプリケーションのより効率的なバージョンに拡張できるように、これを適切に動作させるためにこれを使用して取得できるヘルプをいただければ幸いです。

この件に関して何かアドバイスがあればよろしくお願いします。- アーロン

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
4

3 に答える 3

2

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

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

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

于 2011-09-11T14:25:37.390 に答える
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
于 2011-09-11T07:35:10.383 に答える
0

もし私が一度言ったら、私はそれを百万回言ったことになります。の使用Invokeは、多くの状況で役立ちますが、乱用され、過度に使用されます。ワーカー スレッドの進行状況をユーザーに表示するだけの場合は、使用Invokeが常に最適なオプションとは限りません。そして、ここでも最良の選択肢のようには見えません。

代わりに、割り当てているステータス テキストをoutput、UI スレッド経由でアクセスできる変数に発行します。次に、 aSystem.Windows.Forms.Timerを使用して、その値をより適切なレートで定期的にポーリングします...おそらく1秒ごとに。イベントはTick既に UI スレッドで実行されているため、さまざまな UI コントロールを操作することで、この値を使用してすぐにエンド ユーザーに表示することができます。

文字列は不変であり、本質的にスレッドセーフであるため、スレッドからスレッドへの受け渡しは非常に簡単です。本当に心配しなければならないことは、共有変数に公開された最新の参照が UI スレッドに表示されるようにすることだけです。C# では、volatileこれにキーワードを使用します。Thread.VolatileReadVB では、UI スレッドとThread.VolatileWriteワーカー スレッドから使​​用できます。もちろん、読み取りと書き込みを でラップする方が快適であれば、SyncLockそれもまったく問題ありません。

于 2011-09-12T02:32:04.353 に答える