【问题标题】:VB.Net Await throws a NullReferenceExceptionVB.Net Await 抛出 NullReferenceException
【发布时间】:2021-06-25 04:58:58
【问题描述】:

下面的这段代码不时抛出 NullReferenceException 错误。它并不总是发生,但假设在 10 次尝试中至少有 2-3 次出现这个烦人的“System.NullReferenceException”屏幕。

我正在从数据采集卡 DATAQ 4208U 中读取数据。当涉及到读取的“停止”命令时,会发生此错误。另一个问题是我不是编码和 VB.Net 的高手。

它抛出错误的地方在最后,(当然我只是引用了代码,它并没有到此结束)

等待 TargetDevice.ReadDataAsync(cancelRead.Token)

 Private Async Sub btnState_Click(sender As Object, e As EventArgs) Handles btnState.Click
    If cancelRead IsNot Nothing Then

        'Get here if an acquisition process is in progress and we've been commanded to stop
        cancelRead.Cancel() 'cancel the read process
        cancelRead = Nothing
        Await taskRead 'wait for the read process to complete
        taskRead = Nothing
        Await TargetDevice.AcquisitionStopAsync() 'stop the device from acquiring 

    Else
        'get here if we're starting a new acquisition process
        TargetDevice.Channels.Clear() 'initialize the device
        ConfigureAnalogChannels()
        ConfigureDigitalChannels()

        If SampleRateBad() Then
            'get here if requested sample rate is out of range
            'It's a bust, so...
            btnState.Enabled = True
            Exit Sub
        End If
        'otherwise, the selected sample rate is good, so use it. The class automatically adjusts
        'decimation factor and the protocol's sample rate denominator to yield a sample rate value as close as possible to
        'the value asked for in tbSampleRate.Text. The class also automatically maximizes decimation factor as a function of 
        'channels' AcquisitionMode settings. For this reason Acquisition mode should be defined for all enabled channels
        'before defining sample rate. 
        TargetDevice.SetSampleRateOnChannels(tbSampleRate.Text)

        Try
            Await TargetDevice.InitializeAsync() 'configure the device as defined. Errors if no channels are enabled
        Catch ex As Exception
            'Detect if no channels are enabled, and bail if so. 
            MessageBox.Show("No enabled analog or digital channels.",
                            "Configuration Problem", MessageBoxButtons.OK, MessageBoxIcon.Error)
            btnState.Enabled = True
            Exit Sub
        End Try

        'now determine what sample rate per channel the device is using from the 
        'first enabled input channel, and display it
        Dim FirstInChannel As Dataq.Devices.DI4208.ChannelIn
        Dim NoInputChannels As Boolean = True
        For index = 0 To TargetDevice.Channels.Count - 1
            If TypeOf TargetDevice.Channels(index) Is Dataq.Devices.IChannelIn Then
                FirstInChannel = TargetDevice.Channels(index)
                lblDecimation.Text = FirstInChannel.AcquisitionMode.Samples
                NoInputChannels = False
                Exit For
            End If
        Next
        If NoInputChannels Then
            MessageBox.Show("Please configure at least one analog channel or digital port as an input",
                            "No Inputs Enabled", MessageBoxButtons.OK, MessageBoxIcon.Error)
            btnState.Enabled = True
            Exit Sub
        End If
        'Everything is good, so...
        btnState.Text = "Stop" 'change button text to "Stop" from "Start"
        cancelRead = New CancellationTokenSource() ' Create the cancellation token
        Await TargetDevice.AcquisitionStartAsync() 'start acquiring

        ' NOTE: assumes at least one input channel enabled
        ' Start a task in the background to read data
        taskRead = Task.Run(Async Function()
                                'capture the first channel programmed as an input (MasterChannel)
                                'and use it to track data availability for all input channels
                                Dim MasterChannel As Dataq.Devices.IChannelIn = Nothing
                                For index = 0 To TargetDevice.Channels.Count
                                    If TypeOf TargetDevice.Channels(index) Is Dataq.Devices.IChannelIn Then
                                        MasterChannel = TargetDevice.Channels(index) ' we have our channel 
                                        Exit For
                                    End If
                                Next

                                ' Keep reading while acquiring data
                                While TargetDevice.IsAcquiring
                                    ' Read data and catch if cancelled (to exit loop and continue)
                                    Try
                                        'throws an error if acquisition has been cancelled
                                        'otherwise refreshes the buffer DataIn with new data
                                        'ReadDataAsync moves data from a small, temp buffer between USB hadrware and Windows
                                        'into the SDK's DataIn buffer. ReadDataAsync should be called frequently to prevent a buffer
                                        'overflow at the hardware level. However, buffer DataIn can grow to the size of available RAM if necessary.
                                        Await TargetDevice.ReadDataAsync(cancelRead.Token)
                                    Catch ex As OperationCanceledException
                                        'get here if acquisition cancelled
                                        Exit While
                                    End Try

【问题讨论】:

  • 你能包含一个堆栈跟踪吗?
  • await 不会抛出 NRE。只有尝试使用空变量或字段才能做到这一点。这几乎总是代码中的问题 - 使用变量或参数而不检查它,或者忘记检查方法的返回值。忽略Catch ex As OperationCanceledException 之类的异常是一种 的方法,它最多会导致 NRE,或者在最坏的情况下继续处理不良数据。由于如此糟糕的代码已经损失了数百万美元 - 这没有糖衣
  • 如果您使用硬件,忽略错误很容易导致实际的物理损坏或伤害。解决问题而不是试图掩盖它
  • 此方法的更多问题 - Task.Run 永远不会等待,因此它可能甚至在事件处理程序退出之前都不会启动。到那时,TargetDevice 可能是null。没有理由使用Task.Run 来执行像Await TargetDevice.InitializeAsync() 这样的异步方法。这可以运行 after 无论Task.Run 做什么,假设它确实需要在后台做任何事情。 Catch ex As OperationCanceledException 捕获并隐藏取消并继续循环。为什么?那么ReadDataAsync(cancelRead.Token) 有什么意义呢?

标签: vb.net async-await nullreferenceexception


【解决方案1】:

你的问题在于这个序列:

    cancelRead.Cancel() 'cancel the read process
    cancelRead = Nothing
    Await taskRead 'wait for the read process to complete

如果在您调用 cancelRead.Cancel() 的那一刻,任务当前在 Await TargetDevice.ReadDataAsync 步骤上,这将起作用。

当它在该步骤中不是时会出现问题。在cancelRead 上调用Cancel 并没有什么神奇的作用,特别是它不会在任务中自发地发出OperationCanceledException。它将令牌置于取消状态,因此 OperationCanceledException 将在下次访问时发出...但随后该任务尝试执行 Await TargetDevice.ReadDataAsync 的下一次迭代并弹出 NullRefException,因为您尝试从空的cancelRead 获取Token

如果您将cancelRead 的设置与Await taskRead 交换为空以等待该过程完成,它应该可以解决问题。

另外一个注意事项:Task 实现了IDisposable,所以在设置为Nothing 之前,应该在taskRead 上调用Dispose,否则可能存在资源泄漏的风险。同样对于cancelReadCancellationTokenSource 也实现了IDisposable

【讨论】:

    猜你喜欢
    • 2013-05-30
    • 2020-11-17
    • 2016-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多