2013-06-03 127 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