2017-01-28 21 views
0

デリゲートと呼び出しの仕組みを理解しようとしています。vb.netデリゲートと呼び出し - マルチスレッド

私はラベルとボタンでフォームを作成します。

ボタンをクリックすると、テキストが「停止」に変わり、カウンタがカウントアップを開始します。このカウンターはラベルに表示する必要があります。これは私のコードです:

Public Class Form1 

    Private t1 As Thread 
    Private sek_ As Integer = 0 

    Private Sub btn_read_Click(sender As Object, e As EventArgs) Handles btn_read.Click 


    If btn_read.Text = "Read" Then 

     btn_read.Text = "Stop" 
     t1 = New Thread(AddressOf stopw_) 
     t1.Start() 

    Else 
     lbl_stopw_.Text = "" 
     btn_read.Text = "Read" 

    End If 

    End Sub 

    Private Delegate Sub stopw_D() 

    Private Sub stopw_() 

    Do While btn_read.Text = "Stop" 
     sek_ = sek_ + 1 

     If lbl_stopw_.InvokeRequired Then 
      lbl_stopw_.Invoke(New stopw_D(AddressOf stopw_)) 
     Else 
      lbl_stopw_.Text = sek_ 
     End If 

     Thread.Sleep(1000) 

    Loop 

    sek_ = 0 

    If t1.IsAlive Then t1.Abort() 

    End Sub 

End Class 

デバッグを開始しても、フォームはフリーズし、ラベルは更新されません。デリゲートをすべて削除して何かを呼び出す場合は、Me.CheckForIllegalCrossThreadCalls = Falseの作業が必要です。

私は間違っていますか?

+0

Invoke()呼び出しがかなり間違っています。 * firehose *バグをトリガーすると、UIスレッドはこれらの呼び出しを処理することはできません。もう少しお待ちください。このサイトの名前が表示されます。 BackgroundWorkerを使用して成功のピットに落ちる。 –

答えて

0

Control.Invoke()は、コントロールが作成されたのと同じスレッドでメソッドを実行するために使用されます。コードは各スレッドで一度に1行しか実行できないため、コントロールのスレッドでメソッドを実行すると、そのメソッドの前にそのスレッドの他のコードが完了するまでメソッドが「キューに入れられます。同時実行性の問題がなくなるため、スレッドセーフです。

Delegateは、単にメソッドへのポインタを保持するクラスです。これは、メソッドを通常のオブジェクトのように使用できるように存在します(この場合は関数に渡します)。 AddressOf演算子を使用すると、代理人を簡単に作成できます。

コードにいくつかの問題があります。まず、バックグラウンドスレッドのUI要素にアクセスまたは変更しようとしないでください。ANYコントロールを変更またはチェックする必要がある場合は、常にの呼び出しが必要です。

具体的には、私はあなたのWhile -loopについて話している:

'You can't check the button here without invoking. 
Do While btn_read.Text = "Stop" 

あなたは、スレッドが実行すべき時を示すBoolean変数を作成する場合、それは良いです。

Do While ThreadActive 

は今、別の問題があります:あなたは、スレッドのWhile -loopチェックして、その後、thead要素を開始する前にTrueThreadActiveを設定

Private t1 As Thread 
Private sek_ As Integer = 0 
Private ThreadActive As Boolean = False 

。 NEVER

If lbl_stopw_.InvokeRequired Then 
    lbl_stopw_.Invoke(New stopw_D(AddressOf stopw_)) 

スレッドが実行さ同じメソッドを呼び出す:あなたのUIは、このためにフリーズ!そうすると、再度処理が開始されます。が、UIスレッドにあります。あなたのループは、UIスレッドを完全にビジー状態にしてしまいます。なぜなら、UIスレッド自体が再描画されないからです。

何かを更新するときは、常に別の方法を呼び出します。 .NET 4をターゲットに設定した場合。0以上あなたが迅速、インラインデリゲートのためlambda expressionsを使用することができます。

If lbl_stopw_.InvokeRequired Then 
    lbl_stopw_.Invoke(_ 
     Sub() 
      lbl_stopw_.Text = sek_ 
     End Sub) 
Else 
    lbl_stopw_.Text = sek_ 
End If 

しかし、あなたは、デリゲートを使用する通常の方法に固執する必要が.NET 3.5以下をターゲットにしている場合:

'Outside your thread. 
Private Delegate Sub UpdateLabelDelegate(ByVal Text As String) 

Private Sub UpdateLabel(ByVal Text As String) 
    lbl_stopw_.Text = Text 
End Sub 

'In your thread. 
If lbl_stopw_.InvokeRequired Then 
    lbl_stopw_.Invoke(New UpdateLabelDelegate(AddressOf UpdateLabel), sek_) 
Else 
    UpdateLabel(sek_) 
End If 

また、あなたが書かなければならないコードの量を最小限にするために、あなたはあなたのための呼び出しを行うためにextension methodを作成することができます。

Imports System.Runtime.CompilerServices 

Public Module Extensions 
    <Extension()> _ 
    Public Sub InvokeIfRequired(ByVal Control As Control, ByVal Method As [Delegate], ByVal ParamArray Parameters As Object()) 
     If Parameters Is Nothing OrElse _ 
      Parameters.Length = 0 Then Parameters = Nothing 'If Parameters is null or has a length of zero then no parameters should be passed. 
     If Control.InvokeRequired = True Then 
      Control.Invoke(Method, Parameters) 
     Else 
      Method.DynamicInvoke(Parameters) 
     End If 
    End Sub 
End Module 

使用法、.NET 4.0以上

lbl_stopw_.InvokeIfRequired(_ 
    Sub() 
     lbl_stopw_.Text = sek_ 
    End Sub) 

使用法、.NET 3.5以下

lbl_stopw_.InvokeIfRequired(New UpdateLabelDelegate(AddressOf UpdateLabel), sek_) 

あなたがInvokeRequiredを記述する必要はありません。この拡張メソッドを使用してどこでもチェック:

Do While btn_read.Text = "Stop" 
    sek_ = sek_ + 1 

    lbl_stopw_.InvokeIfRequired(New UpdateLabelDelegate(AddressOf UpdateLabel), sek_) 

    Thread.Sleep(1000) 
Loop 

そして最後に、これは単なる不要です:

If t1.IsAlive Then t1.Abort() 

それはまだstopw_メソッドを終了していないので、それはそのIf -statementに達したときにスレッドが常に生きてます。しかし、スレッドがメソッドを終了すると、通常通り終了しますので、Abort()を呼び出す理由はありません。

答えは少し長くなっていますが、私はそれが参考になることを願っています!

+0

うわー、素晴らしい!私は今それを得たと思う! –

+0

@ azmd108:問題ありません、私は助けることができてうれしいです!あなたのプロジェクトに幸運を祈る! –

関連する問題