设置主线程属性

时间:2015-12-21 16:01:30

标签: vb.net multithreading async-await

我正在传输一个同步运行到异步线程的时钟控件,以提高性能。我是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

2 个答案:

答案 0 :(得分:2)

属性没有线程关联。所以“主线程属性”没有意义。

您可能正在考虑的是从非UI线程访问UI对象实例时发生的跨线程InvalidOperationException。因为在这种情况下,AtomicTime不是UI对象,所以它不具有UI关联性。现在,如果您尝试更新标签或其他内容,那么它将具有UI亲和力,您将获得异常。

异步执行此操作的理想方法是使用真正的异步API,而不是Task.RunTask.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()

排除了错误处理。确保将其包含在生产版本中。