3

計算量の多いタスクを処理する通常のVBの方法は、メインスレッドがUIを処理し続けている間に、バックグラウンドワーカースレッドに配置することです。

なんらかの理由で、これを逆に行う必要があると言います。メインスレッドがうなり声を上げ、バックグラウンドスレッドがUIを更新します。

これが私がこれまでに持っているものです。唯一の問題は、UIウィンドウ(Form1)が再描画されている間は、操作したり、移動したりサイズを変更したりすることもできないことです(マウスカーソルが砂時計に変わり、クリックしません)。

Public Class ProgressDisplay

Private trd As Thread

    Public Sub New()
        trd = New Thread(AddressOf threadtask)
        trd.Start()
    End Sub

    Private Sub threadtask()
        Dim f1 As Form1
        f1 = New Form1
        f1.Show()
        Do
            f1.Update()
            Thread.Sleep(100)
        Loop
    End Sub

End Class

編集:理想的には、このようなインターフェースをクライアントに提示する必要があります

Public Class ProgressDisplay
    Public Sub New()
    Public Sub Update(byval progress as int)
End Class

クライアントはこれを次のように呼び出します(実際には、COMを介したアンマネージC ++では、画像が表示されます)。

Dim prog = new ProgressDisplay()
DoLotsOfWork(addressof prog.update) ' DoLotsOfWork method takes a callback argument to keep client informed of progress
4

3 に答える 3

4

問題を明確に言い換えるには、独自のプログラム内でそれを使用するクライアントにビジュアルコンポーネントを提供する必要があります。クライアントのプログラムは、ユーザーの制御の及ばないところで、そのメイン(つまり:UI)スレッドを拘束し、クライアントのプログラムがフリーズしている間、ビジュアルコンポーネントを引き続き機能させる必要があります。

ここで明確にしましょう-クライアントのプログラムがフリーズしているという事実は彼らの問題であり、それは彼らのアプリケーションコードの欠陥の直接の結果です。以下に続くのはハックであり、誰も使用してはならないひどいものです。したがって、次のことを行うことはできますが、それが推奨される解決策と見なされることはほとんどありません。それはすべてのベストプラクティスに直面して飛ぶ。

そうは言っても、メインUIスレッドの横で実行を継続できる2番目のアプリケーションコンテキストと新しいメッセージループを作成する必要があります。このようなクラスは機能します:

Imports System.Threading

Public Class SecondUIClass

    Private appCtx As ApplicationContext
    Private formStep As Form
    Private trd As Thread
    Private pgBar As ProgressBar
    Delegate Sub dlgStepIt()

    Public Sub New()
        trd = New Thread(AddressOf NewUIThread)
        trd.SetApartmentState(ApartmentState.STA)
        trd.IsBackground = True
        trd.Start()
    End Sub

    Private Sub NewUIThread()
        formStep = New Form()
        pgBar = New ProgressBar()
        formStep.Controls.Add(pgBar)
        appCtx = New ApplicationContext(formStep)
        Application.Run(appCtx)
    End Sub

    Public Sub StepTheBar()
        formStep.Invoke(New dlgStepIt(AddressOf tStepIt))
    End Sub

    Private Sub tStepIt()
        pgBar.PerformStep()
    End Sub

End Class

基本的に、上記のクラスで行っているのは、新しいSTAスレッド内に新しいアプリケーションコンテキストを作成することです(そのスレッドにメッセージループを与えます)。そのコンテキストには、メインUIスレッドとは別に動作し続けることができるメインフォーム(スレッドの所有権とメッセージ処理の責任を与える)が含まれています。これは、プログラム内にプログラムを配置するのとよく似ています。2つのUIスレッドには、それぞれが相互に排他的なコントロールのセットがあります。

新しいUIスレッド(またはそのフォーム)が所有するコントロールのいずれかと対話する呼び出しは、Control.Invoke新しいUIスレッドが対話を行うものであることを確認するために、プライマリUIスレッド(または他のスレッド)からマーシャリングする必要があります。ここでも使用できBeginInvokeます。

このクラスにはクリーンアップコードや安全性のチェックなどがなく(警告)、正常に終了するかどうかさえわかりません。そのタスクはあなたに任せます。これは、開始する方法を示しています。メインフォームでは、次のようなことを行います。

Public Class Form1

    Private pgClass As New SecondUIClass

    Private Sub Button1_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles Button1.Click
        Dim i As Integer
        For i = 1 To 10
            System.Threading.Thread.Sleep(1000)
            pgClass.StepTheBar()
        Next
    End Sub
End Class

上記のアプリケーションを実行すると、Form1によって作成された2番目のフォームと同様に作成されpgClassます。をクリックするとButton1、ループを通過している間Form1がロックされますが、2番目のフォームは引き続き有効で応答性が高く、が呼び出されるForm1たびに進行状況バーが更新されます。Form1.StepTheBar()

この場合の最善の解決策は、実際には、「クライアント」に適切なプログラミング方法を学習させ、そもそもこの難問にとらわれないようにすることです。それが完全に不可能であり、あなたが彼らのために彼らの貧弱なコードにもかかわらず生き続けるコンポーネントを作成しなければならない場合、上記のアプローチはおそらくあなたの唯一の頼みの綱です。

于 2012-06-22T12:43:05.877 に答える
3

UIは、それを作成したスレッドを介してのみ更新できます。どのスレッドも、UIスレッドでメソッドを呼び出すように要求して、Control.Invokeメソッドを使用してUIを更新できるようにすることができますが、UIスレッドがビジーでなくなるまで待機します。UIスレッドがビジー状態の場合、UIは誰もが更新することはできません。Application.RunUIは、メインメッセージループ(AKA )がキュー内のウィンドウメッセージを処理し、それらに作用する場合にのみ更新されます。UIスレッドがビジー状態であるか、ループでスタックしている場合、またはサーバーからの応答を待機している場合、UIスレッドはそれらのウィンドウメッセージを処理できません(DoEvents、可能であれば、絶対にお勧めしません)。したがって、はい、UIがビジー状態の間、ロックされます。これが、誰もが別のスレッドで大量のビジネスロジックを実行することを提案する理由です。それが問題ではなかったとしたら、なぜ誰かがわざわざワーカースレッドを作成するのでしょうか。

于 2012-06-22T12:00:47.080 に答える
2

UIは、メインイベントディスパッチスレッド以外のスレッドから更新することはできません。だからあなたはうまくいかないことをしようとしています。

于 2012-06-22T11:54:50.730 に答える