【问题标题】:Continuously reading from serial port asynchronously properly正确地从串口异步连续读取
【发布时间】:2016-08-03 00:07:49
【问题描述】:

我只想说我今天才开始尝试使用 Async,所以我对自己在做什么的理解非常有限。

我之前使用线程从串行端口读取数据,将该数据处理为要写入的数据,然后再写入。我有一个线程读取数据并将其放入缓冲区,另一个线程处理来自缓冲区的数据,最后一个线程写入它。这样,如果我点击一个按钮,我可以发送额外的数据。

现在我只是想学习并确保我做的一切都正确,所以我尝试从串行端口读取数据,并将该数据添加到多行文本框中。代码如下:

连接串口,如果成功,调用UpdateMessageBox异步:

private void serialConnectClick(object sender, EventArgs e)
{
    if (!_serialConnected)
    {
        _serialConnected = SerialConnect(portCombo.Text, int.Parse(baudCombo.Text));
        if (!_serialConnected)
        {
            portCombo.SelectedIndex = 0;
            messageTextBox.Text += "Failed to connect.\r\n";
            return;
        }

        serialConnectBtn.Text = "Disconnect";
        serialStatusLabel.Text = "Serial: Connected";
        serialStatusLabel.ForeColor = Color.Blue;
        messageTextBox.Text += "Connected\r\n";
        VDIportCombo.Enabled = false;
        soundSuccess.Play();
        UpdateMessageBox(); // this is an async function
    }
}

这会不断调用ReadLineAsync并将结果添加到文本框:

public async Task UpdateMessageBox()
{
    messageTextBox.Text += "Reading data from serial.";
    while (_serialConnected)
    {
        string message = await SerialReadLineAsync(serial);
        messageTextBox.Text += message;
    }
}

这在SerialPort.BaseStream 上执行ReadAsync,并且仅在我们得到整行时返回数据(由换行符表示):

async Task<string> SerialReadLineAsync(SerialPort serialPort)
{
    byte[] buffer = new byte[1];
    string result = string.Empty;
    Debug.WriteLine("Let's start reading.");

    while (true)
    {
        await serialPort.BaseStream.ReadAsync(buffer, 0, 1);
        result += serialPort.Encoding.GetString(buffer);

        if (result.EndsWith(serialPort.NewLine))
        {
            result = result.Substring(0, result.Length - serialPort.NewLine.Length);
            result.TrimEnd('\r','\n');
            Debug.Write(string.Format("Data: {0}", result));
            result += "\r\n";
            return result;
        }
    }
}

我做的每一件事都正确吗?我可以从 UI 线程调用异步方法吗? Visual Studios 告诉我我应该使用 await,但这只是建议。

我希望其他代码在 UI 线程持续读取时运行,所以我不想 await UpdateMessageBox 函数。如果这是一个线程,我只想让读取线程在后台运行,我只会做myReadThread.Start(),异步有类似的东西吗?

编辑:澄清一下,这段代码确实有效,但我想知道这是否是我正在做的事情的“正确”方式。

【问题讨论】:

    标签: c# multithreading asynchronous serial-port


    【解决方案1】:

    你需要改变:

    private void serialConnectClick(object sender, EventArgs e)
    

    private async void serialConnectClick(object sender, EventArgs e)
    

    ...并将对 UpdateMessageBox(); 的调用更改为:

    await UpdateMessageBox().ConfigureAwait(true);
    

    UpdateMessageBox 应该使用 ConfigureAwait(true) 来捕获当前上下文 (UI),否则 messageTextBox.Text += message; 将在不同的线程上执行:

    public async Task UpdateMessageBox()
    {
        messageTextBox.Text += "Reading data from serial.";
        while (_serialConnected)
        {
            string message = await SerialReadLineAsync(serial).ConfigureAwait(true);
            messageTextBox.Text += message;
        }
    }
    

    SerialReadLineAsync,你可以更改:

    await serialPort.BaseStream.ReadAsync(buffer, 0, 1);
    

    ...到:

    await serialPort.BaseStream.ReadAsync(buffer, 0, 1).ConfigureAwait(false);
    

    ...因为SerialReadLineAsync 语句与 UI 无关。

    一般提示是“一直异步”,这意味着awaits 和async 方法的任何方法也需要asyncawaited。在调用堆栈中重复此操作。

    【讨论】:

    • 啊,不知道我可以让那个函数异步!有没有办法我不必await UpdateMessageBox?当我包含await 关键字时,当我第二次按下该按钮(当前被编程为关闭串行端口)时,我得到IOexceptions
    • 对于未来的任何人,你都可以!只需添加.ConfigureAwait(false); 所以应该是UpdateMessageBox().ConfigureAwait(false);
    • 你当然可以。在 UI 方法中使用 ConfigureAwait(false) 时要小心,否则紧随其后的语句可能会在不同的线程上执行。否则,如果您不关心使用什么线程,请使用ConfigureAwait(false)Tell me more
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-27
    • 1970-01-01
    • 1970-01-01
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 2013-09-19
    相关资源
    最近更新 更多