我正在传输一个同步运行到异步线程的时钟控件,以提高性能。我是async / await的新手,但我非常熟悉" old"方法(Thread / Threadstart / InvokeRequired / Begininvoke)......
我原以为这会立即抛出一个错误,并且必须添加一个回调等等。但是一切都在运行而没有错误。我的问题是,这样可以,还是可能会遇到线程错误?
我基本上只是将整个函数移动到一个新函数中,并在原始函数中使用异步调用来调用它。原始功能恰好是" GetTimeAsync()"之一。
主线程属性被称为" AtomicTime" ...... 等待返回,然后点击" Dim s为String ="""调用 - 所以看起来它看起来行为正常,我只是为什么它没有抛出一个交叉线程错误。这是在幕后处理的#34;现在还是什么?
我看了很多线程试图找到答案 - 如果我错过了一个,我会道歉。您可以想象,搜索结果会带来许多问题,涵盖许多实际错误。这已经得到了解答,我只是错过了它。
Private Async Sub GetTime()
Await Task.Run(Sub() GetTimeAsync())
Dim s As String = ""
End Sub
Private Sub GetTimeAsync()
Try
Dim ntpServer As String = "wwv.nist.gov"
Dim ntpData(47) As Byte
ntpData(0) = &H1B
Dim serverReplayTime As Byte = Convert.ToByte(40)
Dim addresses = Dns.GetHostEntry(ntpServer).AddressList
Dim EndP As IPEndPoint = New IPEndPoint(addresses(0), 123)
Dim soc As Socket = New Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp)
soc.ReceiveTimeout = 3000
soc.Connect(EndP)
soc.Send(ntpData)
soc.Receive(ntpData)
soc.Close()
soc.Dispose()
Dim intPart As UInt32 = BitConverter.ToUInt32(ntpData, serverReplayTime)
Dim fractPart As UInt32 = BitConverter.ToUInt32(ntpData, serverReplayTime + 4)
intPart = SwapEndianness(intPart)
fractPart = SwapEndianness(fractPart)
Dim mills As Object = (intPart * 1000) + ((fractPart * 1000) / &H100000000L)
Dim networkDateTime As Object = (New DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds(mills)
Dim localTime As DateTime = networkDateTime.ToLocalTime()
AtomicTime = localTime '***HERE*** This works... Should it????
addresses = Nothing
EndP = Nothing
networkDateTime = Nothing
Catch ex As Exception
Dim s As String = ex.ToString()
End Try
End Sub
答案 0 :(得分:2)
属性没有线程关联。所以“主线程属性”没有意义。
您可能正在考虑的是从非UI线程访问UI对象实例时发生的跨线程InvalidOperationException
。因为在这种情况下,AtomicTime
不是UI对象,所以它不具有UI关联性。现在,如果您尝试更新标签或其他内容,那么它将具有UI亲和力,您将获得异常。
异步执行此操作的理想方法是使用真正的异步API,而不是Task.Run
。 Task.Run
很好(仅适用于UI应用程序),如果你有CPU限制的工作要做,或者只有可用的同步API,但它更多的是后备,使用它,如果你有的话解。纯异步代码更好。
我的博客上有async
intro,可以帮助您入门。
答案 1 :(得分:0)
我使用了主要线程注释的Mark建议 - 使用提供的套接字异步函数。这需要进行更多的一些研究,所以我将发布代码(同样,欢迎评论......这完全来自我在网上找到的代码示例,但它正在运行 - 所以我正在调用这个解决了,即使它在技术上不使用async / await函数/运算符,它使用内置的ins,这在可用时可能更好。
首先,我们需要一个connect,send和receive事件来连接回调 - 这些将是表单级变量。
Public Class YourClass
Private ConnectDoneEvent As System.Threading.ManualResetEvent
Private SendDoneEvent As System.Threading.ManualResetEvent
Private ReceiveDoneEvent As System.Threading.ManualResetEvent
'...
End Class
不要忘记初始化以避免空引用异常。它们在初始化时采用布尔值来确定是否设置了块。您可能希望它取消设置/ false,但要验证这一点,以免在程序启动时立即阻塞线程。
接下来,我们需要启动连接,等待它,一旦它回来,启动发送,等待......接收。我觉得这是关于这次体验最酷的部分之一 - 这非常简单......我们有3个功能可以用于每个事件的回调(连接,发送,接收)。它们完全相同:
Private Sub ConnectCallback(ar As IAsyncResult)
Try
Dim soc As Socket = CType(ar.AsyncState, Socket)
soc.EndConnect(ar)
ConnectDoneEvent.Set()
Catch ex As Exception
End Try
End Sub
Private Sub SendCallback(ar As IAsyncResult)
Try
Dim soc As Socket = CType(ar.AsyncState, Socket)
Dim iBytesSent As Integer = soc.EndSend(ar)
SendDoneEvent.Set()
Catch ex As Exception
End Try
End Sub
Private Sub ReceiveCallback(ar As IAsyncResult)
Try
Dim soc As Socket = CType(ar.AsyncState, Socket)
Dim iBytesRcvd As Integer = soc.EndReceive(ar)
ReceiveDoneEvent.Set()
Catch ex As Exception
End Try
End Sub
现在,我们需要做的就是拨打电话。这非常简单
Dim ntpServer As String = "wwv.nist.gov"
Dim ntpData(47) As Byte
ntpData(0) = &H1B
Dim serverReplayTime As Byte = Convert.ToByte(40)
Dim addresses = Dns.GetHostEntry(ntpServer).AddressList
Dim EndP As IPEndPoint = New IPEndPoint(addresses(0), 123)
Dim soc As Socket = New Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp)
soc.ReceiveTimeout = 3000
soc.BeginConnect(EndP, AddressOf ConnectCallback, soc)
ConnectDoneEvent.WaitOne()
soc.BeginSend(ntpData, 0, ntpData.Length, SocketFlags.None, AddressOf SendCallback, soc)
SendDoneEvent.WaitOne()
soc.BeginReceive(ntpData, 0, ntpData.Length, SocketFlags.None, AddressOf ReceiveCallback, soc)
ReceiveDoneEvent.WaitOne()
soc.Close()
soc.Dispose()
排除了错误处理。确保将其包含在生产版本中。