提问人:isilter 提问时间:3/29/2021 更新时间:3/30/2021 访问量:132
VB.Net Await 引发 NullReferenceException
VB.Net Await throws a NullReferenceException
问:
下面的代码会不时引发 NullReferenceException 错误。它并不总是发生,但比方说,在 2 次尝试中至少有 3-10 次我得到这个烦人的“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
答:
您的问题出在以下序列上:
cancelRead.Cancel() 'cancel the read process
cancelRead = Nothing
Await taskRead 'wait for the read process to complete
如果在调用 时,任务当前在步骤上,这将起作用。cancelRead.Cancel()
Await TargetDevice.ReadDataAsync
当它不在该步骤上时,就会出现问题。调用不会做任何神奇的事情,特别是,它不会自发地在任务中发出。它将令牌置于已取消状态,因此 OperationCanceledException
将在下次访问时发出...但随后任务尝试执行 的下一次迭代并弹出 NullRefException,因为您尝试从 null .Cancel
cancelRead
OperationCanceledException
Await TargetDevice.ReadDataAsync
Token
cancelRead
如果将设置为“无”以等待该过程完成,则应该可以解决问题。cancelRead
Await taskRead
还有一点需要注意:implements ,所以你应该在将其设置为 之前调用,否则可能会有泄漏资源的风险。同样,对于 ,也实现了 。Task
IDisposable
Dispose
taskRead
Nothing
cancelRead
CancellationTokenSource
IDisposable
评论
await
不抛出 NRE。仅尝试使用 null 变量或字段才能执行此操作。这几乎总是代码中的一个问题 - 使用变量或参数而不检查它,或者忘记检查方法的返回值。忽略这样的异常是导致 NRE 的好方法,或者在最坏的情况下继续处理错误数据的好方法。由于如此糟糕的代码,已经损失了数百万美元——这没有糖衣炮弹Catch ex As OperationCanceledException
Task.Run
TargetDevice
null
Task.Run
Await TargetDevice.InitializeAsync()
Task.Run
Catch ex As OperationCanceledException
ReadDataAsync(cancelRead.Token)