DataWriter.DetachStream() 抛出 'System.Runtime.InteropServices.COMException'
DataWriter.DetachStream() throws 'System.Runtime.InteropServices.COMException'
我正在为 Raspberry Pi 创建一个 UWP 程序。该程序的功能之一是从 Arduino 发送和接收一些数据。
问题是当我多次尝试快速向 Arduino 发送数据时,我最终得到 System.Runtime.InteropServices.COMException The operation identifier is not valid.
来自 DataWriter.DetachStream()
。
快速发送数据效果很好,直到达到一定数量时,我才抛出异常。
对于 "rapid",我的意思是使用自动点击器点击按钮以每毫秒发送一次数据。
我没有尝试连续多次缓慢发送数据来重现问题,因为这可能需要很长时间(看到它需要大约 10-20 秒,传输之间有 1 毫秒的延迟。
我已经为这个问题寻找了很多小时的解决方案,但我似乎找不到任何相关的 questions/solutions。
public sealed partial class LightControl : Page
{
int Alpha;
int Red;
int Green;
int Blue;
// This is the handler for the button to send data
private void LightButton_Click(object sender, RoutedEventArgs e)
{
if (!(sender is Button button) || button.Tag == null) return;
string tag = button.Tag.ToString();
Alpha = int.Parse(tag.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
Red = int.Parse(tag.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
Green = int.Parse(tag.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
Blue = int.Parse(tag.Substring(6, 2), System.Globalization.NumberStyles.HexNumber);
SendLightData();
}
public async void SendLightData()
{
await ArduinoHandler.Current.WriteAsync(ArduinoHandler.DataEnum.LightArduino,
ArduinoHandler.DataEnum.Light, Convert.ToByte(LightConstants.LightCommand.LightCommand),
Convert.ToByte(Red), Convert.ToByte(Green), Convert.ToByte(Blue), Convert.ToByte(Alpha),
WriteCancellationTokenSource.Token);
}
}
public class ArduinoHandler
{
// Code for singleton behaviour. Included for completeness
#region Singleton behaviour
private static ArduinoHandler arduinoHandler;
private static Object singletonCreationLock = new Object();
public static ArduinoHandler Current
{
get
{
if (arduinoHandler == null)
{
lock (singletonCreationLock)
{
if (arduinoHandler == null)
{
CreateNewArduinoHandler();
}
}
}
return arduinoHandler;
}
}
public static void CreateNewArduinoHandler()
{
arduinoHandler = new ArduinoHandler();
}
#endregion
private DataWriter dataWriter;
private Object WriteCancelLock = new Object();
public async Task WriteAsync(DataEnum receiver, DataEnum sender,
byte commandByte1, byte dataByte1, byte dataByte2, byte dataByte3,
byte dataByte4, CancellationToken cancellationToken)
{
try
{
dataWriter = new DataWriter(arduinos[receiver].OutputStream);
byte[] buffer;
Task<uint> storeAsyncTask;
lock (WriteCancelLock)
{
buffer = new byte[8];
buffer[0] = Convert.ToByte(receiver);
buffer[1] = Convert.ToByte(sender);
buffer[2] = commandByte1;
buffer[3] = dataByte1;
buffer[4] = dataByte2;
buffer[5] = dataByte3;
buffer[6] = dataByte4;
buffer[7] = Convert.ToByte('\n');
cancellationToken.ThrowIfCancellationRequested();
dataWriter.WriteBytes(buffer);
storeAsyncTask = dataWriter.StoreAsync().AsTask(cancellationToken);
}
uint bytesWritten = await storeAsyncTask;
Debug.Write("\nSent: " + BitConverter.ToString(buffer) + "\n");
}
catch (Exception e)
{
Debug.Write(e.Message);
}
finally
{
dataWriter.DetachStream(); // <--- I've located the exception to originate from here, using the debugger in Visual Studio
dataWriter.Dispose();
}
}
public enum DataEnum
{
Light = 0x01,
Piston = 0x02,
PC = 0x03,
LightArduino = 0x04
}
}
本以为Raspberry Pi可以将数据发送给Arduino,但是在快速数据传输一段时间后,抛出异常。
更新
我尝试按照下面的建议为 dataWriter 使用局部变量,但这会在快速数据传输一段时间后导致奇怪的行为。就好像它变慢了一样。值得注意的是,我再也没有得到异常。
很难解释它的行为方式,但 Debug.Write 记录了我发送的消息(工作正常)。然而,过了一会儿,它似乎 "slow down",即使我停止点击后,数据仍然每秒发送一次。到目前为止,它完全可以正常工作。所以我想知道我是否达到了某种限制?
更新 2
我似乎找到了一个相当 "hacky" 和奇怪的问题解决方案。
如果我在 Arduino 上使用 Serial.write() 将数据发送回 Raspberry Pi,它似乎已经以某种方式解决了这个问题。
如果有人知道这是如何工作的,我很想知道:)
const int payloadSize = 8;
byte payload[payloadSize]
int numBytes;
// Called each time serial data is available
void serialEvent()
{
numBytes = Serial.available();
if (numBytes == payloadSize)
{
for (int i = 0; i < payloadSize; i++)
{
payload[i] = Serial.read();
Serial.write(payload[i]); // <--- This line fixed the issue for whatever reason
}
}
checkData(); // Function to do something with the data
for (int i = 0; i < payloadSize; i++)
{
payload[i] = None;
}
numBytes = 0;
}
您的问题源于这样一个事实,即您使用的是使用 async
方法的即发即弃方法。当您快速连续调用 SendLightData()
时,它不会等待前一个 WriteAsync
操作完成。
一旦执行到第一个实际的 await
表达式 - 即 await storeAsyncTask
行,UI 线程将被释放以处理另一个按钮点击。
单击这个新按钮可以开始执行并覆盖 ArduinoHandler
同一实例中的 dataWriter
字段。当第一个 storeAsyncTask
完成执行时,它实际上会 datach 第二次调用的 dataWriter
,而不是它自己的。这可能会导致多种不同类型的问题和竞争条件。
因此您必须确保在 之前的操作实际执行之前 无法单击按钮。您可以为此使用一个布尔标志作为一个简单的解决方案。
private bool _isWorking = false;
public async void SendLightData()
{
if (!_isWorking)
{
try
{
_isWorking = true;
await ArduinoHandler.Current.WriteAsync(ArduinoHandler.DataEnum.LightArduino,
ArduinoHandler.DataEnum.Light, Convert.ToByte(LightConstants.LightCommand.LightCommand),
Convert.ToByte(Red), Convert.ToByte(Green), Convert.ToByte(Blue), Convert.ToByte(Alpha),
WriteCancellationTokenSource.Token);
}
finally
{
_isWorking = false;
}
}
这将确保两个操作永远不会同时执行。
其他解决方案可能是不将数据写入器存储为字段,而是将其作为局部变量。当您避免调用之间的所有共享状态时,您可以放心地知道不会因覆盖而产生竞争条件。
我正在为 Raspberry Pi 创建一个 UWP 程序。该程序的功能之一是从 Arduino 发送和接收一些数据。
问题是当我多次尝试快速向 Arduino 发送数据时,我最终得到 System.Runtime.InteropServices.COMException The operation identifier is not valid.
来自 DataWriter.DetachStream()
。
快速发送数据效果很好,直到达到一定数量时,我才抛出异常。 对于 "rapid",我的意思是使用自动点击器点击按钮以每毫秒发送一次数据。
我没有尝试连续多次缓慢发送数据来重现问题,因为这可能需要很长时间(看到它需要大约 10-20 秒,传输之间有 1 毫秒的延迟。
我已经为这个问题寻找了很多小时的解决方案,但我似乎找不到任何相关的 questions/solutions。
public sealed partial class LightControl : Page
{
int Alpha;
int Red;
int Green;
int Blue;
// This is the handler for the button to send data
private void LightButton_Click(object sender, RoutedEventArgs e)
{
if (!(sender is Button button) || button.Tag == null) return;
string tag = button.Tag.ToString();
Alpha = int.Parse(tag.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
Red = int.Parse(tag.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
Green = int.Parse(tag.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
Blue = int.Parse(tag.Substring(6, 2), System.Globalization.NumberStyles.HexNumber);
SendLightData();
}
public async void SendLightData()
{
await ArduinoHandler.Current.WriteAsync(ArduinoHandler.DataEnum.LightArduino,
ArduinoHandler.DataEnum.Light, Convert.ToByte(LightConstants.LightCommand.LightCommand),
Convert.ToByte(Red), Convert.ToByte(Green), Convert.ToByte(Blue), Convert.ToByte(Alpha),
WriteCancellationTokenSource.Token);
}
}
public class ArduinoHandler
{
// Code for singleton behaviour. Included for completeness
#region Singleton behaviour
private static ArduinoHandler arduinoHandler;
private static Object singletonCreationLock = new Object();
public static ArduinoHandler Current
{
get
{
if (arduinoHandler == null)
{
lock (singletonCreationLock)
{
if (arduinoHandler == null)
{
CreateNewArduinoHandler();
}
}
}
return arduinoHandler;
}
}
public static void CreateNewArduinoHandler()
{
arduinoHandler = new ArduinoHandler();
}
#endregion
private DataWriter dataWriter;
private Object WriteCancelLock = new Object();
public async Task WriteAsync(DataEnum receiver, DataEnum sender,
byte commandByte1, byte dataByte1, byte dataByte2, byte dataByte3,
byte dataByte4, CancellationToken cancellationToken)
{
try
{
dataWriter = new DataWriter(arduinos[receiver].OutputStream);
byte[] buffer;
Task<uint> storeAsyncTask;
lock (WriteCancelLock)
{
buffer = new byte[8];
buffer[0] = Convert.ToByte(receiver);
buffer[1] = Convert.ToByte(sender);
buffer[2] = commandByte1;
buffer[3] = dataByte1;
buffer[4] = dataByte2;
buffer[5] = dataByte3;
buffer[6] = dataByte4;
buffer[7] = Convert.ToByte('\n');
cancellationToken.ThrowIfCancellationRequested();
dataWriter.WriteBytes(buffer);
storeAsyncTask = dataWriter.StoreAsync().AsTask(cancellationToken);
}
uint bytesWritten = await storeAsyncTask;
Debug.Write("\nSent: " + BitConverter.ToString(buffer) + "\n");
}
catch (Exception e)
{
Debug.Write(e.Message);
}
finally
{
dataWriter.DetachStream(); // <--- I've located the exception to originate from here, using the debugger in Visual Studio
dataWriter.Dispose();
}
}
public enum DataEnum
{
Light = 0x01,
Piston = 0x02,
PC = 0x03,
LightArduino = 0x04
}
}
本以为Raspberry Pi可以将数据发送给Arduino,但是在快速数据传输一段时间后,抛出异常。
更新
我尝试按照下面的建议为 dataWriter 使用局部变量,但这会在快速数据传输一段时间后导致奇怪的行为。就好像它变慢了一样。值得注意的是,我再也没有得到异常。
很难解释它的行为方式,但 Debug.Write 记录了我发送的消息(工作正常)。然而,过了一会儿,它似乎 "slow down",即使我停止点击后,数据仍然每秒发送一次。到目前为止,它完全可以正常工作。所以我想知道我是否达到了某种限制?
更新 2
我似乎找到了一个相当 "hacky" 和奇怪的问题解决方案。 如果我在 Arduino 上使用 Serial.write() 将数据发送回 Raspberry Pi,它似乎已经以某种方式解决了这个问题。
如果有人知道这是如何工作的,我很想知道:)
const int payloadSize = 8;
byte payload[payloadSize]
int numBytes;
// Called each time serial data is available
void serialEvent()
{
numBytes = Serial.available();
if (numBytes == payloadSize)
{
for (int i = 0; i < payloadSize; i++)
{
payload[i] = Serial.read();
Serial.write(payload[i]); // <--- This line fixed the issue for whatever reason
}
}
checkData(); // Function to do something with the data
for (int i = 0; i < payloadSize; i++)
{
payload[i] = None;
}
numBytes = 0;
}
您的问题源于这样一个事实,即您使用的是使用 async
方法的即发即弃方法。当您快速连续调用 SendLightData()
时,它不会等待前一个 WriteAsync
操作完成。
一旦执行到第一个实际的 await
表达式 - 即 await storeAsyncTask
行,UI 线程将被释放以处理另一个按钮点击。
单击这个新按钮可以开始执行并覆盖 ArduinoHandler
同一实例中的 dataWriter
字段。当第一个 storeAsyncTask
完成执行时,它实际上会 datach 第二次调用的 dataWriter
,而不是它自己的。这可能会导致多种不同类型的问题和竞争条件。
因此您必须确保在 之前的操作实际执行之前 无法单击按钮。您可以为此使用一个布尔标志作为一个简单的解决方案。
private bool _isWorking = false;
public async void SendLightData()
{
if (!_isWorking)
{
try
{
_isWorking = true;
await ArduinoHandler.Current.WriteAsync(ArduinoHandler.DataEnum.LightArduino,
ArduinoHandler.DataEnum.Light, Convert.ToByte(LightConstants.LightCommand.LightCommand),
Convert.ToByte(Red), Convert.ToByte(Green), Convert.ToByte(Blue), Convert.ToByte(Alpha),
WriteCancellationTokenSource.Token);
}
finally
{
_isWorking = false;
}
}
这将确保两个操作永远不会同时执行。
其他解决方案可能是不将数据写入器存储为字段,而是将其作为局部变量。当您避免调用之间的所有共享状态时,您可以放心地知道不会因覆盖而产生竞争条件。