제 질문이 너무 길면 미리 사과드립니다. "다른 클래스의 스레드에 의해 수신되는 메시지로 GUI의 데이터를 업데이트하는 방법"이라는 질문을 보았습니다.이 질문은 제가 수행하려고 시도한 것에 매우 가깝지만 답변이 도움이 될 정도로 자세하지 않았습니다.Worker Thread에서 Main (UI) 스레드로 전환 할 수 있습니까?
VB6 앱을 VB.NET (VS2013)으로 변환했습니다. 이 응용 프로그램의 주요 기능은 쿼리를 Linux 서버에 보내고 결과를 호출 양식에 표시하는 것입니다. WinSock 컨트롤은 더 이상 존재하지 않으므로 TcpClient 클래스와 관련된 함수를 처리하는 클래스를 만들었습니다. 성공적으로 서버에 연결하여 데이터를 보내고받을 수 있습니다.
문제는이 클래스를 사용하여 서버에 쿼리 메시지를 보내는 여러 가지 양식이 있다는 것입니다. 서버는 호출 양식에 표시 할 데이터로 응답합니다. 폼의 컨트롤을 업데이트하려고하면 "크로스 스레드 작업이 유효하지 않습니다 : 컨트롤 x가 만들어진 스레드 이외의 스레드에서 액세스되었습니다."라는 오류 메시지가 나타납니다. Control.InvokeRequired와 함께 Control.Invoke를 사용하여 Main/UI 스레드의 컨트롤을 업데이트해야하지만 VB에서 좋은 예제를 찾을 수 없습니다. 또한 각 폼에 다양한 컨트롤이있는 50 개 이상의 폼이 있으며 각 컨트롤에 대한 대리자 처리기를 쓰고 싶지 않습니다. 쓰레드와 델리게이트의 개념이 나에게 새롭다는 것도 언급해야합니다. 나는 지난 주 또는 2 주 동안 내가이 주제에서 찾을 수있는 모든 것을 읽었지 만, 나는 여전히 붙어있다!
메인 스레드로 다시 전환 할 수있는 방법이 있습니까? 그렇지 않다면 Control.Invoke를 사용하여 한 번에 여러 컨트롤을 처리 할 수있는 방법이 있습니까?
데이터를 보내고 받기 시작하기 전에 연결 직후 스레드를 시작했지만 netback.BeginRead는 콜백 함수가 실행되면 자체 스레드를 시작합니다. 또한 BeginRead 대신 Read를 사용해 보았습니다. 응답에 많은 양의 데이터가있는 경우 제대로 작동하지 않습니다. BeginRead는 더 잘 처리했습니다. Dorothy가 오즈에 머물러있는 것처럼 느껴진다. 나는 단지 메인 스레드에 집에 가고 싶다.
제공 할 수있는 도움에 미리 감사드립니다.
Option Explicit On
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
Friend Class ATISTcpClient
Public Event Receive(ByVal data As String)
Private Shared WithEvents oRlogin As TcpClient
Private netStream As NetworkStream
Private BUFFER_SIZE As Integer = 8192
Private DataBuffer(BUFFER_SIZE) As Byte
Public Sub Connect()
Try
oRlogin = New Net.Sockets.TcpClient
Dim localIP As IPAddress = IPAddress.Parse(myIPAddress)
Dim localPrt As Int16 = myLocalPort
Dim ipLocalEndPoint As New IPEndPoint(localIP, localPrt)
oRlogin = New TcpClient(ipLocalEndPoint)
oRlogin.NoDelay = True
oRlogin.Connect(RemoteHost, RemotePort)
Catch e As ArgumentNullException
Debug.Print("ArgumentNullException: {0}", e)
Catch e As Net.Sockets.SocketException
Debug.Print("SocketException: {0}", e)
End Try
If oRlogin.Connected() Then
netStream = oRlogin.GetStream
If netStream.CanRead Then
netStream.BeginRead(DataBuffer, 0, BUFFER_SIZE, _
AddressOf DataArrival, DataBuffer)
End If
Send(vbNullChar)
Send(User & vbNullChar)
Send(User & vbNullChar)
Send(Term & vbNullChar)
End If
End Sub
Public Sub Send(newData As String)
On Error GoTo send_err
If netStream.CanWrite Then
Dim sendBytes As [Byte]() = Encoding.UTF8.GetBytes(newData)
netStream.Write(sendBytes, 0, sendBytes.Length)
End If
Exit Sub
send_err:
Debug.Print("Error in Send: " & Err.Number & " " & Err.Description)
End Sub
Private Sub DataArrival(ByVal dr As IAsyncResult)
'This is where it switches to a WorkerThread. It never switches back!
On Error GoTo dataArrival_err
Dim myReadBuffer(BUFFER_SIZE) As Byte
Dim myData As String = ""
Dim numberOfBytesRead As Integer = 0
numberOfBytesRead = netStream.EndRead(dr)
myReadBuffer = DataBuffer
myData = myData & Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)
Do While netStream.DataAvailable
numberOfBytesRead = netStream.Read(myReadBuffer, 0, myReadBuffer.Length)
myData = myData & Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)
Loop
'Send data back to calling form
RaiseEvent Receive(myData)
'Start reading again in case we don‘t have the entire response yet
If netStream.CanRead Then
netStream.BeginRead(DataBuffer, 0,BUFFER_SIZE,AddressOf DataArrival,DataBuffer)
End If
Exit Sub
dataArrival_err:
Debug.Print("Error in DataArrival: " & err.Number & err.Description)
End Sub
[비동기 프로그래밍] (http://msdn.microsoft.com/en-us/library/hh191443.aspx)을 살펴볼 수 있습니다. Nitpicking :'On Error GoTo'는'Try ... Catch'로 변환되어야하고'Dim myReadBuffer (BUFFER_SIZE) As Byte'는 원하는 것보다 하나 많은 배열 요소를 할당합니다 (이것은'BUFFER_SIZE - 1'이어야합니다). –
나는 내 대답을 특정 정보가 아니라 주로 링크이기 때문에 의견을 옮기기로 결정했습니다. 코드가 폼에 있고 그렇다면 'InvokeRequired'와 'Invoke'를 사용하십시오. 다음은 단계별 솔루션을 작성하는 방법에 대한 전체 설명입니다. http://www.vbforums.com/showthread.php?498387-Accessing-Controls-from-Worker-Threads 코드가없는 경우 양식을 사용하면 해당 멤버에 액세스 할 수 없습니다. 이 경우'SynchronizationContext' 클래스를 사용해야합니다. 위의 링크는 이후 게시물에서 그 사용 예를 제공합니다. – jmcilhinney