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

时间:2013-05-01 21:13:32

标签: .net vb.net multithreading winforms

我现在正在维护一个用VB.net(.net 3.5)编写的软件,它通过网络与另一个我无法控制的软件进行通信。我对WinForms软件或最佳实践不是很熟悉,但这是我对该程序的理解:

当用户通过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线程的问题。

如果没有Threading.Sleep在UI线程中如何处理?模态形式是处理这种情况的方式吗?

提前致谢!

1 个答案:

答案 0 :(得分:0)

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

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