更新:FOund错误。我的代码按预期工作,但我的同事在我正在与之通信的设备上添加了一个小代码。结果是它忽略了我的第二个命令,因为它正忙于做其他事情。 修正了它,现在又恢复了工作。
在本周初,我问了一个关于将串口限制只限于一个实例的问题。
有人在那里真的帮了我,并向我提到了SemaphoreSlim
。
这当然真的成功了,我认为就是这样。
现在几天后我终于解决了另一个主要问题,在测试时我注意到信号灯不再工作了。
我将我的代码回滚到之前的版本,我刚刚实现了信号量,它确实在哪里工作。 但即便是那段代码也不再适用了。
我在Visual Studio,PC或Raspberry中没有任何改变...
有没有人知道为什么理论上应该工作的东西不再起作用?
非常感谢这里的帮助:)
添加了我正在使用的代码的非常短的版本。 通常我会处理我进入Int或字符串数组的数据。 我也有一个CRC16验证,一切都将包含在try / catch块中,以便在它们发生时捕获异常。
但是,这是迄今为止我需要的一切。 如果需要,我会尝试提供更详细的信息。请告诉我。
目前的行为: 第一项任务开始并正常工作。 第二个任务没有开始,之后的每个新任务也都没有开始。
预期行为: 启动第一个任务并完成它。 完成第一个任务后,加载第二个任务并完成它。 当我在那之后开始另一个任务时,它也应该运行它。
MainPage.xaml中
public MainPage()
{
this.InitializeComponent();
InitPort();
}
private async void getData_Click(object sender, RoutedEventArgs e)
{
// Call the async operations.
// I usually don't call the same operation twice at the same time but it's just a test here.
// In normal usage the data contains different sizes.
await getData();
await getData();
}
private async Task getData()
{
ReadData readData = new ReadData();
byte[] readOutput = await readData.ReadParaBlock();
DisplayTextBox.Text = BitConverter.ToString(readOutput);
}
public async void InitPort()
{
string success = await ReadWriteAdapter.Current.Init();
DisplayTextBox.Text = success;
}
ReadData.cs
public class ReadData
{
private ReadBlock readBlock = new ReadBlock();
public async Task<byte[]> ReadParaBlock()
{
// Load task into the semaphore
await ReadWriteAdapter.Current.semaphore.WaitAsync();
// start writing to device
await readBlock.Write();
// dropped check of recieved checksum because obsolete for test
// start reading from device
byte[] recievedArray = await readBlock.Read();
// release the task from semaphore
ReadWriteAdapter.Current.semaphore.Release();
return recievedArray;
}
}
ReadBlock.cs
public class ReadBlock
{
public async Task<uint> Write()
{
// Command sent to device to get data
byte[] WriteArray = System.Text.Encoding.ASCII.GetBytes("getdata");
return await ReadWriteAdapter.Current.WriteAsync(WriteArray);
}
public async Task<byte[]> Read()
{
byte[] ListenOut = await ReadWriteAdapter.Current.Listen(100);
// dropped data conversion because obsolete for test
return ListenOut;
}
}
ReadWriteAdapter.cs
public class ReadWriteAdapter
{
public SemaphoreSlim semaphore { get; private set; }
private static readonly Object lockObject = new object();
private static ReadWriteAdapter instance;
private DataWriter dataWriter = null;
private DataReader dataReader = null;
private SerialDevice serialPort = null;
public static ReadWriteAdapter Current
{
get
{
if (instance == null)
{
lock (lockObject)
{
if (instance == null)
{
instance = new ReadWriteAdapter();
}
}
}
return instance;
}
}
private ReadWriteAdapter()
{
this.semaphore = new SemaphoreSlim(1, 1);
}
// initialize the serial port and configure it
public async Task<string> Init()
{
string aqs = SerialDevice.GetDeviceSelector();
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(aqs, null);
if (devices.Any())
{
string deviceId = devices[0].Id;
serialPort = await SerialDevice.FromIdAsync(deviceId);
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.BaudRate = 19200;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
serialPort.Handshake = SerialHandshake.None;
dataWriter = new DataWriter(serialPort.OutputStream);
dataReader = new DataReader(serialPort.InputStream);
}
return "found port";
}
// start to listen to the serial port
public async Task<byte[]> Listen(uint BufferLength)
{
byte[] listen = new byte[BufferLength];
dataReader = new DataReader(serialPort.InputStream);
listen = await ReadAsync(BufferLength);
if (dataReader != null)
{
dataReader.DetachStream();
dataReader.Dispose();
dataReader = null;
}
return listen;
}
// function to read and interpret the data from serial port
private async Task<byte[]> ReadAsync(uint ReadBufferLength)
{
Task<uint> loadAsyncTask;
byte[] returnArray = new byte[ReadBufferLength];
dataReader.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReader.LoadAsync(ReadBufferLength).AsTask();
uint bytesRead = await loadAsyncTask;
if (bytesRead > 0)
{
dataReader.ReadBytes(returnArray);
}
return returnArray;
}
// write the data using the serial port
public async Task<uint> WriteAsync(byte[] data)
{
dataWriter.WriteBytes(data);
Task<uint> storeAsyncTask = dataWriter.StoreAsync().AsTask();
return await storeAsyncTask;
}
}
答案 0 :(得分:0)
这看起来应该可以工作,事实上,修改为使用MemoryStream
而不是串口的版本可以正常工作。我无法访问您正在使用的I / O API,因此我不了解它们应该如何使用。然而,信号量似乎做得很好。您可以在下方找到示例的简化版本,其中的按钮可以串行或并行运行GetData()
。
如果信号量存在问题,则只能是您未在Release()
内调用finally
。否则,您对信号量的使用看起来很好。
要查看信号量是否正常工作,您只需将初始值设定项更改为new SemaphoreSlim(8, 8)
,然后看到线程相互冲突。
<强> ReadBlock.cs:强>
public class ReadBlock {
private static int nextString;
private static readonly string[] strings = {
"ONE ", "TWO ", "THREE", "FOUR ",
"FIVE ", "SIX ", "SEVEN", "EIGHT"
};
public static async Task<byte[]> ReadParaBlock() {
var id = Interlocked.Increment(ref nextString) - 1;
var name = strings[id % strings.Length];
try
{
await ReadWriteAdapter.Current.Semaphore.WaitAsync();
Trace.WriteLine($"[{name.Trim()}] Entered Semaphore");
await Write(Encoding.ASCII.GetBytes("TEST_" + name));
return await Read();
}
finally {
Trace.WriteLine($"[{name.Trim()}] Exiting Semaphore");
ReadWriteAdapter.Current.Semaphore.Release();
}
}
public static async Task<uint> Write(byte[] bytes) =>
await ReadWriteAdapter.Current.WriteAsync(bytes);
public static async Task<byte[]> Read() => await ReadWriteAdapter.Current.Listen(10);
}
<强> ReadWriteAdapter.cs:强>
public class ReadWriteAdapter {
private static ReadWriteAdapter instance;
public static ReadWriteAdapter Current
=> LazyInitializer.EnsureInitialized(
ref instance,
() => new ReadWriteAdapter());
private readonly MemoryStream stream = new MemoryStream();
public SemaphoreSlim Semaphore { get; } = new SemaphoreSlim(1, 1);
public async Task<string> Init()
=> await Task.FromResult("found port");
public async Task<byte[]> Listen(uint bufferLength)
=> await ReadAsync(bufferLength);
private async Task<byte[]> ReadAsync(uint readBufferLength) {
await Task.CompletedTask;
var returnArray = new byte[readBufferLength];
await stream.ReadAsync(returnArray, 0, returnArray.Length);
return returnArray;
}
public async Task<uint> WriteAsync(byte[] data) {
stream.SetLength(stream.Capacity);
stream.Position = 0;
await Task.Delay(1);
await stream.WriteAsync(data, 0, data.Length);
stream.SetLength(data.Length);
stream.Position = 0;
return (uint)data.Length;
}
}
<强> MainWindow.xaml.cs:强>
public partial class MainWindow {
private class TextBoxTraceListener : TraceListener {
private readonly TextBoxBase _textBox;
public TextBoxTraceListener(TextBoxBase textBox)
=> _textBox = textBox;
public override void WriteLine(string message)
=> Write(message + Environment.NewLine);
public override void Write(string message) {
_textBox.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => _textBox.AppendText(message)));
}
}
public MainWindow() {
TaskScheduler.UnobservedTaskException +=
(s, e) => Trace.TraceError(e.ToString());
InitializeComponent();
Trace.Listeners.Add(new TextBoxTraceListener(textBox));
InitPort();
}
private async void InitPort() {
textBox.AppendText(await ReadWriteAdapter.Current.Init());
}
private async void OnGetDataInSerialClick(object sender, RoutedEventArgs e) {
textBox.Clear();
for (var i = 0; i < 8; i++) await GetData();
}
private async void OnGetDataInParallelClick(object sender, RoutedEventArgs e) {
textBox.Clear();
await Task.WhenAll(Enumerable.Range(0, 8).Select(i => GetData()));
}
private async Task GetData() {
await Task.Delay(50).ConfigureAwait(false); // Get off the UI thread.
Trace.WriteLine(Encoding.ASCII.GetString(await ReadBlock.ReadParaBlock()));
}
}
<强> MainWindow.xaml:强>
<Window x:Class="WpfTest2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Bottom" Click="OnGetDataInSerialClick"
Content="Load Data in Serial" />
<Button DockPanel.Dock="Bottom" Click="OnGetDataInParallelClick"
Content="Load Data in Parallel" />
<TextBox x:Name="textBox" TextWrapping="Wrap" />
</DockPanel>
</Window>