我現在正在維護一個用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線程的問題。
如何在不使用線程處理的情況下處理這個問題?模式是如何處理這個問題的?
在此先感謝!
最佳做法是*從不*阻止UI線程。如果您需要阻止用戶採取措施,請禁用用戶界面的交互部分,直到用戶再次使用它們的安全爲止。禁用UI的一個選項是啓動一個模式「進度」對話框(最後提到),但它不是唯一的選擇。 – dlev 2013-05-01 21:14:54
BackgroudWorker with ReportsProgress – Paparazzi 2013-05-01 21:21:29