2013-06-03 126 views
1

也許這是一個簡單的問題,我只是不知道正確的搜索條件來找到答案,但我的Google-fu失敗了我在這一個。從後臺線程打開一個模態窗體來阻塞UI線程而不阻止後臺線程

我的vb.net應用程序有一個控制所有套接字通信的後臺線程。有時候,我需要這個通信線程來打開一個模態窗體來顯示一條消息並阻止UI交互,直到通信線程完成一系列任務之後,通信線程將移除模態窗體,從而允許用戶繼續交互。

目前,我的通信類包含後臺線程有兩個事件,StartBlockingTask和EndBlockingTask。我的主窗體具有這些事件的事件監聽器,這些事件調用了like-named subs。他們叫代碼看起來像這樣:

Private Delegate Sub BlockingDelegate(ByVal reason As String) 

Private Sub StartBlockingTask(ByVal reason As String) 
    If Me.InvokeRequired Then 
     Dim del As New BlockingDelegate(AddressOf StartBlockingTask) 
     Me.Invoke(del, New Object() {reason}) 
    Else 
     Try 
      _frmBlock.lblBlock.Text = reason 
      _frmBlock.ShowDialog() 
     Catch ex As Exception 
      'stuff 
     End Try 
    End If 
End Sub 

Private Sub EndBlockingTask() 
    If Me.InvokeRequired Then 
     Dim del As New BlockingDelegate(AddressOf EndBlockingTask) 
     Me.Invoke(del, New Object() {""}) 
    Else 
     Try 
      If (Not _frmBlock Is Nothing) Then 
       _frmBlock.DialogResult = Windows.Forms.DialogResult.OK 
      End If 
     Catch ex As Exception 
      'stuff 
     End Try 
    End If 
End Sub 

這成功地阻止從交互用戶界面,但它也能阻止通信線程,因此EndBlockingTask事件從來沒有真正得到提高。如何從通信線程打開此模式對話框並允許通信線程繼續運行?

在此先感謝!

回答

3

我不同意。

所有需要做的事情就是將Invoke()更改爲BeginInvoke()並且你是黃金。

這是因爲Invoke()實際上是同步的,導致它阻塞,直到ShowDialog()解決。

使用的BeginInvoke()使得異步和允許在線程繼續被封鎖的UI:

Public Class Form1 

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click 
     If Not BackgroundWorker1.IsBusy Then 
      BackgroundWorker1.RunWorkerAsync() 
     End If 
    End Sub 

    Private Delegate Sub BlockingDelegate(ByVal reason As String) 

    Private Sub StartBlockingTask(ByVal reason As String) 
     If Me.InvokeRequired Then 
      Dim del As New BlockingDelegate(AddressOf StartBlockingTask) 
      Me.BeginInvoke(del, New Object() {reason}) 
     Else 
      Try 
       _frmBlock.lblBlock.Text = reason 
       _frmBlock.ShowDialog() 
      Catch ex As Exception 
       'stuff 
      End Try 
     End If 
    End Sub 

    Private Sub EndBlockingTask() 
     If Me.InvokeRequired Then 
      Dim del As New BlockingDelegate(AddressOf EndBlockingTask) 
      Me.BeginInvoke(del, New Object() {""}) 
     Else 
      Try 
       If (Not _frmBlock Is Nothing) Then 
        _frmBlock.DialogResult = Windows.Forms.DialogResult.OK 
       End If 
      Catch ex As Exception 
       'stuff 
      End Try 
     End If 
    End Sub 

    Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork 
     For i As Integer = 1 To 10 
      BackgroundWorker1.ReportProgress(i) 
      System.Threading.Thread.Sleep(1000) 

      If i = 4 Then 
       Dim del As New BlockingDelegate(AddressOf StartBlockingTask) 
       del("bada...") 
      ElseIf i = 7 Then 
       Dim del As New BlockingDelegate(AddressOf EndBlockingTask) 
       del("bing!") 
      End If 
     Next 
    End Sub 

    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged 
     Label1.Text = e.ProgressPercentage 
    End Sub 

End Class 
+1

我回到這裏重新閱讀我給出的答案,因爲重新安排了一些代碼後,被調用的方法開始再次阻塞線程。你的評論立即修復它。謝謝。我現在明白了Invoke和BeginInvoke之間的區別! – e2579382

+1

這是一種大多數新的線程都不能實現的區別,因爲現在的計算機如此之快,以至於Invoke()調用看起來不是異步的。您的場景就是演示其差異的最佳例子! –

1

您正在從創建的子地址中調用地址。地址需要從此子外部調用。

Private Sub StartBlockingTask(ByVal reason As String) 
    If Me.InvokeRequired Then 
     Dim del As New BlockingDelegate(AddressOf StartBlockingTask) 


    Private Sub EndBlockingTask() 
     If Me.InvokeRequired Then 
      Dim del As New BlockingDelegate(AddressOf EndBlockingTask) 

您需要創建兩個代表。一個用於StartBlockingTask,一個用於EndBlockingTask

這是從MSDN的例子,

Delegate Sub MySubDelegate(ByVal x As Integer) 
Protected Sub Test() 
    Dim c2 As New class2() 
    ' Test the delegate. 
    c2.DelegateTest() 
End Sub 

Class class1 
    Sub Sub1(ByVal x As Integer) 
     MessageBox.Show("The value of x is: " & CStr(x)) 
    End Sub 
End Class 

Class class2 
    Sub DelegateTest() 
     Dim c1 As Class1 
     Dim msd As MySubDelegate 
     c1 = New Class1() 
     ' Create an instance of the delegate. 
     msd = AddressOf c1.Sub1 
     msd.Invoke(10) ' Call the method. 
    End Sub 
End Class 

http://msdn.microsoft.com/en-us/library/5t38cb9x(v=vs.71).aspx

讓我知道,如果這有助於。

+1

啊!謝謝。我完全按照你所描述的完成了,它完美地運作了! – e2579382

+0

完成!並再次感謝! – e2579382