在等待网络活动时阻塞UI线程

问题描述:

我现在正在维护一个用VB.net(.net 3.5)编写的软件,它通过网络与另一个我无法控制的其他软件进行通信。我对WinForms软件或最佳实践不是很熟悉,但是我对此程序的理解如下:在等待网络活动时阻塞UI线程

当用户通过UI执行某些操作时,程序会向主机进程发送消息并等待回复的响应,处理响应的结果,然后允许用户继续。在客户端和主机之间进行对话时,UI必须被锁定。这个对话可能需要两到三秒钟,这取决于主持人的压力。

这个软件的作者在这个通信正在进行时用while循环来阻塞UI线程。显然,这不是一个好方法,因为它可能会导致Windows报告程序已锁定并提供退出选项。

我的问题是,这样做的最佳做法是什么。 Winforms程序有几个窗口,上面有数百个按钮和控件,在通讯过程中所有按钮都必须被阻止。通信(和等待)是由于数百种不同的可能的用户与程序的交互而发生的。那么,在通讯过程中,我怎样才能阻止整个程序接受输入,按键,键盘等?

下面是一些关于它当前工作的精简代码。我不害怕重写这个程序的通信部分。

Private clientSocket As New System.Net.Sockets.TcpClient() 
Private serverStream As NetworkStream 
Private readThread As Thread = New Thread(AddressOf readLoop) 



Public Sub Connect() 
    'This function called to start the connection when program starts 
    Try 
     clientSocket.Connect(_Address, _Port) 
     serverStream = clientSocket.GetStream() 

     readThread.IsBackground = True 
     readThreadActive = True 
     readThread.Start() 


    Catch ex As Exception 
     RaiseEvent communicationsErrorEvent("CONNECT: " & ex.Message.ToString) 
    End Try 
End Sub 

Public Sub WriterLogOn(ByVal UserName As String, ByVal Password As String) 
    'This Sub is called from a button press on a WinForm 

    Dim xmlString As String = "blah" 'generate xmlstring to send to host including username and encrypted password 

    communicationsState = state_WriterLogOn 'store the state from an enum 
    'each communication type has its own enum 

    write(xmlString) 

    While communicationsState = state_WriterLogOn 
     Thread.Sleep(1) 
    End While 
End Sub 


Private Sub write(ByVal writeString As String) 
    Try 
     Dim outStream As Byte() = System.Text.Encoding.ASCII.GetBytes(writeString & Chr(0)) 
     serverStream.Write(outStream, 0, outStream.Length) 
     serverStream.Flush() 
    Catch ex As Exception 
     RaiseEvent communicationsErrorEvent("WRITE: " & ex.Message) 
    End Try 
End Sub 


Private Sub readLoop() 

    Try 
     While (readThreadActive) 'always true unless changed in certain conditions 

      Dim buffSize As Integer 
      Dim inStream(10024) As Byte 
      buffSize = clientSocket.ReceiveBufferSize 
      Dim numberOfBytesRead As Integer = 0 
      Dim returnDataBuilder As StringBuilder = New StringBuilder 

      Do 
       numberOfBytesRead = serverStream.Read(inStream, 0, inStream.Length) 
       returnDataBuilder.AppendFormat("{0}", Encoding.ASCII.GetString(inStream, 0, numberOfBytesRead)) 
      Loop While serverStream.DataAvailable 

      Dim returnData As String = returnDataBuilder.ToString() 


      If (returnData.Length > 0) Then 
       ParseMessage(returnData) 
      End If 

      Threading.Thread.Sleep(1) 

     End While 

    Catch ex As Exception 
     RaiseEvent communicationsErrorEvent("READLOOP: " & ex.Message) 
    End Try 

End Sub 


Private Sub ParseMessage(ByVal readdata As String) 
    'This function parses the string received and does the appropriate things with it 
    'And when appropriate it sets the state back to idle, allowing the UI thread to unblock 
    communicationsState = state_Idle 
End Sub 

读取循环必须在任何时候运行,因为它也可以在必须在后台,而不会阻塞UI线程处理的任何时间收到请自来的邮件。 (这是从ParseMessage()子)完成的。

所以,我足够清楚知道这是处理阻塞UI线程的不好方法。但是在四处搜索之后,我还没有找到一个很好的解决方案来停止所有用户输入的应用程序范围,直到其他线程收到的其他通信释放它为止。

我已经尝试将代码插入各种UI方法,检查状态并停止按钮/键盘/等做任何事情,但有很多不同的地方我需要插入此代码,感觉就像错误的方式去做吧。我已经搜索过,但只发现有关如何不阻止UI线程的问题。

如何在不使用线程处理的情况下处理这个问题?模式是如何处理这个问题的?

在此先感谢!

+0

最佳做法是*从不*阻止UI线程。如果您需要阻止用户采取措施,请禁用用户界面的交互部分,直到用户再次使用它们的安全为止。禁用UI的一个选项是启动一个模式“进度”对话框(最后提到),但它不是唯一的选择。 – dlev 2013-05-01 21:14:54

+0

BackgroudWorker with ReportsProgress – Paparazzi 2013-05-01 21:21:29

如何禁用窗体上的所有控件,然后显示用户反馈以声明长时间运行的操作正在进行 - 可能是永无止境的进度条?您可以使用下面的功能,只需从需要的地方调用即可。

Public Sub DisableUI(frm as form) 

For Each c as Control in frm.controls 

'determine if control is already disabled and set a flag for later 

If c.Enabled = False Then 

c.Tag = "1" 

Else 

c.Enabled = False 

End If 

Next 

End Sub 

Public Sub EnabledUI(frm as form) 

For Each c as Control in frm.controls 

If c.Tag.toString = "1" Then 

'Control should stay disabled 

Else 

c.Enabled = True 

End If 

Next 

End Sub