繁体   English   中英

C# SerialPort BaseStream ReadAsync - CancellationToken 从未取消?

[英]C# SerialPort BaseStream ReadAsync - CancellationToken never cancelled?

我尝试以异步方式从串行端口读取数据,记住操作时间不得超过指定时间段。 我使用的代码:

private async Task<int> Read(byte[] buffer, int offset, int length)
    {
        Console.WriteLine("Reading response to buffer of size {0}, offset {1}, max to cnt {2}", buffer.Length, offset, length);

        int totalBytesRead = 0;

        Console.WriteLine("Begin reading...");

        while (true)
        {
            Console.WriteLine("Re-entering read");

            int bytesRead = 0;

            using (CancellationTokenSource cts = new CancellationTokenSource(new TimeSpan(0, 0, 0, 0, 333))) // 333 ms
            {
                Console.WriteLine("Waiting for read to complete...");

                int tmpOffset = offset + totalBytesRead;
                int tmpLength = length - totalBytesRead;
                Console.WriteLine("tmpOffset: {0}, tmpLength: {1}", tmpOffset, tmpLength);

                try
                {
                    bytesRead = await mPort.BaseStream.ReadAsync(buffer, tmpOffset, tmpLength, cts.Token);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception during read: {0}, stack trace: {1}", e.Message, e.StackTrace);
                    break;
                }
                Console.WriteLine("Read completed");
            }

            totalBytesRead += bytesRead;

            Console.WriteLine("Bytes read: {0}, totalBytesRead: {1}, buffer: {2}", bytesRead, totalBytesRead, BitConverter.ToString(buffer, 0, buffer.Length));

            if (bytesRead == 0)
            {
                break;
            }
        }

        Console.WriteLine("Read finished");

        return totalBytesRead;
    }

以下代码将这些消息返回到控制台:

Reading response to buffer of size 256, offset 0, max to cnt 256
Begin reading...
Re-entering read
Waiting for read to complete...
tmpOffset: 0, tmpLength: 256
Read completed
Bytes read: 1, totalBytesRead: 1, buffer: 52-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
Re-entering read
Waiting for read to complete...
tmpOffset: 1, tmpLength: 255
Read completed
Bytes read: 8, totalBytesRead: 9, buffer: 52-45-4A-30-30-36-4A-44-52-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
Re-entering read
Waiting for read to complete...
tmpOffset: 9, tmpLength: 247

在最后一条消息之后它挂起。 当然超过 333ms 的超时时间是(应该)使用的。 对我来说,令牌似乎从未被取消,或者至少从未捕获过此取消。 我 99% 确定设备不再发送任何数据,这就是为什么我指望超时触发。 我是 C# 的新手,所以也许我不太明白。 你能看出任务没有在此处取消的任何原因吗?

我试图复制和调整中提出的方法这个文章。

这是因为 ReadAsync 方法的内部实现(第 417 行)。 基本上,在方法的入口点对取消令牌进行检查,但随后它(取消令牌)不会传递到后续调用(“一路”)。 因此,如果您幸运地在执行到达对 ReadAsync 的调用之前取消,您将获得预期的行为。

我不打算讨论为什么他们选择不让它可取消(在其他帖子上,我现在不记得了,我读到这是一种安全预防措施,所以不要让缓冲区处于某种时髦的状态)。

你可以做的是用某种计时器来包装调用,或者它是如何为我工作的:

cancelToken.Register(() => mport.Dispose());

因为我想要的是永远结束事情。

这引起了我的

IOException: 由于线程退出或应用程序请求,I/O 操作已中止。

我捕获并处理了它(在我的情况下,我抛出了一个 OperationCanceledException - 但仅针对此 IOException,而不是任何)。

关于 SerialPort 流通信还有更多令人讨厌的东西。 其他一些有用的东西: http : //www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport

我最近遇到了同样的问题,试图通过串行线路与一些旧的嵌入式仪器进行通信。 我的解决方案是一组看起来像这样的扩展方法,我没有处理整个端口,关闭基本流似乎就足够了,然后如果在取消时它是打开的,我会立即重新打开它。

public static class SerialAsyncExtensions
{
    public static async Task<int> ReadSerialAsync(this SerialPort f_Serial, byte[] f_Buffer, int f_Offset, int f_Count, int f_Delay)
    {
        if (f_Delay > 0)
            await Task.Delay(f_Delay);

        return await f_Serial.BaseStream.ReadAsync(f_Buffer, f_Offset, f_Count);
    }

    public static async Task<int> ReadSerialAsync(this SerialPort f_Serial, byte[] f_Buffer, int f_Offset, int f_Count, int f_Delay, CancellationToken f_Token)
    {
        CancellationTokenRegistration l_Token_Registration = f_Token.Register((param) =>
        {
            SerialPort l_Serial = (SerialPort)param;

            l_Serial.DiscardInBuffer();

        }, f_Serial);

        if (f_Delay > 0)
            await Task.Delay(f_Delay, f_Token);

        try
        {
            int l_Result = await f_Serial.BaseStream.ReadAsync(f_Buffer, f_Offset, f_Count, f_Token);

            return l_Result;
        }
        catch(System.IO.IOException Ex)
        {
            throw new OperationCanceledException("ReadSerialAsync operation Cancelled.", Ex);
        }
        finally
        {
            l_Token_Registration.Dispose();
        }
    }

    public static async Task WriteSerialAsync(this SerialPort f_Serial, byte[] f_Buffer, int f_Offset, int f_Count)
    {
        await f_Serial.BaseStream.WriteAsync(f_Buffer, f_Offset, f_Count);
    }

    public static async Task WriteSerialAsync(this SerialPort f_Serial, byte[] f_Buffer, int f_Offset, int f_Count, CancellationToken f_Token)
    {
        CancellationTokenRegistration l_Token_Registration = f_Token.Register((param) =>
        {
            SerialPort l_Serial = (SerialPort)param;

            l_Serial.DiscardOutBuffer();

        }, f_Serial);

        try
        {
            await f_Serial.BaseStream.WriteAsync(f_Buffer, f_Offset, f_Count, f_Token);
        }
        catch(System.IO.IOException Ex)
        {
            throw new OperationCanceledException("WriteSerialAsync operation Cancelled.", Ex);
        }
        finally
        {
            l_Token_Registration.Dispose();
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM