C#在串行端口通信中等待事件和超时 [英] C# await event and timeout in serial port communication
问题描述
我在串行端口上的通讯很简单,所有内容都是根据书籍和文档而定,因此开放端口方法如下:
Hi I have a simple communication on serial port well all is according to book and documentation so open port method looks like this:
public SerialPort OpenPort(string portName)
{
Port = new SerialPort(portName, BaudRate);
try
{
Port.Open();
Port.DtrEnable = true;
Port.RtsEnable = true;
Port.DataReceived += DataReceivedEvent;
}
catch (Exception e)
{
Console.WriteLine($"ERRROR: {e.Message}");
}
return Port;
}
此处有一个数据读取事件:
Here we have an event on data read:
private async void DataReceivedEvent(object sender, SerialDataReceivedEventArgs e)
{
var data = new byte[Port.BytesToRead];
await Port.BaseStream.ReadAsync(data, 0, data.Length);
Response = data;
isFinished = true;
}
一切都很好,但现在我想发送一条消息需求并在属性中存储响应,我也想在该任务超时上添加取消令牌。所以我想出了这种方法:
Well all is fine and dandy, but now i want to send a message on demand and store response in a property, also i want to add cancellation token on that task timeout. So i came up with this method:
public async Task SendMessenge(byte[] messange)
{
var cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;
cancellationTokenSource.CancelAfter(5000);
token.ThrowIfCancellationRequested();
isFinished = false;
try
{
Task worker = Task.Run(() =>
{
while (!isFinished)
{
}
}, token);
await Port.BaseStream.WriteAsync(messange, 0, messange.Length, token);
await worker;
}
catch (OperationCanceledException e)
{
throw new OperationCanceledException(e.Message, e, token);
}
}
这个while循环存在问题,如果这是任务它进入无休止的循环,并且它不捕获超时令牌,如果我将其放置在任务之外并删除工作程序,则它可以工作,但是失去了取消令牌。我想我可以做一些手动倒计时,例如:
Problem is with this while loop, if it is task it goes into endless loop, and it does not capture timeout token, if i put it outside a task and remove worker it works but im loosing cancellation token. I guess i could do some manual countdown like:
double WaitTimeout = Timeout + DateAndTime.Now.TimeOfDay.TotalMilliseconds;
while (!(DateAndTime.Now.TimeOfDay.TotalMilliseconds >= WaitTimeout)|| !isFalse)
但是它看起来很丑。
所以我认为我的基本问题是如何有效地等待事件响应并获得超时?
So i think my basic question is how to effectively await for event to response and get a timeout?
推荐答案
在写操作后循环读取数据,直到获得完整的响应。但是您需要使用同步API和 Task.Run()
,因为异步API的当前版本会忽略 SerialPort
超时属性完全基于任务的API中的 CancellationToken
。
Read data in a loop after write operation until get a full response. But you need to use synchronous API and Task.Run()
as current version of the asynchronous API ignores SerialPort
timeout properties completely and CancellationToken
in Task based API almost completely.
摘录自 SerialPort.ReadTimeout 与 SerialPort.BaseStream.ReadAsync()
相关的Microsoft文档它使用默认实现 Stream.ReadAsync()
:
Excerpt from the SerialPort.ReadTimeout Microsoft Docs that is relevant to SerialPort.BaseStream.ReadAsync()
because it uses default implementation Stream.ReadAsync()
:
此属性不影响 BeginRead
方法rel = nofollow noreferrer> BaseStream 属性。
This property does not affect the BeginRead method of the stream returned by the BaseStream property.
使用同步API和动态超时属性的示例实现更新:
Example implementation using synchronous API and dynamic timeout properties update:
static byte[] SendMessage(byte[] message, TimeSpan timeout)
{
// Use stopwatch to update SerialPort.ReadTimeout and SerialPort.WriteTimeout
// as we go.
var stopwatch = Stopwatch.StartNew();
// Organize critical section for logical operations using some standard .NET tool.
lock (_syncRoot)
{
var originalWriteTimeout = _serialPort.WriteTimeout;
var originalReadTimeout = _serialPort.ReadTimeout;
try
{
// Start logical request.
_serialPort.WriteTimeout = (int)Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0);
_serialPort.Write(message, 0, message.Length);
// Expected response length. Look for the constant value from
// the device communication protocol specification or extract
// from the response header (first response bytes) if there is
// any specified in the protocol.
int count = ...;
byte[] buffer = new byte[count];
int offset = 0;
// Loop until we recieve a full response.
while (count > 0)
{
_serialPort.ReadTimeout = (int)Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0);
var readCount = _serialPort.Read(buffer, offset, count);
offset += readCount;
count -= readCount;
}
return buffer;
}
finally
{
// Restore SerialPort state.
_serialPort.ReadTimeout = originalReadTimeout;
_serialPort.WriteTimeout = originalWriteTimeout;
}
}
}
示例用法:
byte[] request = ...;
TimeSpan timeout = ...;
var sendTask = Task.Run(() => SendMessage(request, timeout));
try
{
await await Task.WhenAny(sendTask, Task.Delay(timeout));
}
catch (TaskCanceledException)
{
throw new TimeoutException();
}
byte[] response = await sendTask;
您可以使用 CancellationToken
实例执行类似操作并在读写操作之间使用 CancellationToken.ThrowIfCancellationRequested()
,但必须确保在 SerialPort
否则,线程池线程将永远挂着并持有锁。据我所知,您不能使用 CancellationToken.Register()
,因为没有 SerialPort
方法可以调用取消操作。
You can do similar thing with CancellationToken
instance and use CancellationToken.ThrowIfCancellationRequested()
between read and write operations but you have to make sure that proper timeouts are set on SerialPort
or otherwise Thread pool thread will hang forever possible holding a lock. As far as I know you can't utilize CancellationToken.Register()
because there is no SerialPort
method to call to cancel an operation.
有关更多信息,请检查:
For more information check:
- 金·汉密尔顿的前5个SerialPort提示 / li>
- 建议使用SerialPort异步使用模式,< a href = https://github.com/dotnet/corefx/issues/25154 rel = nofollow noreferrer> Stream.ReadAsync()中的CancellationToken是建议性文档和 NetworkStream.ReadAsync / WriteAsync忽略了.NET GitHub上与CancellationToken 相关的问题
- < a href = https://devblogs.microsoft.com/pfxteam/should-i-expose-asynchronous-wrappers-for-synchronous-methods/ rel = nofollow noreferrer>我应该为同步方法公开异步包装吗? Stephen Toub的文章
- Top 5 SerialPort Tips article by Kim Hamilton
- Recommended asynchronous usage pattern of SerialPort, Document that CancellationToken in Stream.ReadAsync() is advisory and NetworkStream.ReadAsync/WriteAsync ignores CancellationToken related issues on .NET GitHub
- Should I expose asynchronous wrappers for synchronous methods? article by Stephen Toub
这篇关于C#在串行端口通信中等待事件和超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!