连续正确地从串口异步读取
Continuously reading from serial port asynchronously properly
首先我要说我今天才开始尝试使用 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
,当我们得到整行(用换行符表示)时只有 returns 数据:
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()
,异步有类似的东西吗?
编辑: 澄清一下,这段代码确实有效,但我想知道这是否是 "proper" 做我正在做的事情的方式。
您需要更改:
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.
无关
一般提示是"async all the way",这意味着任何await
是async
方法的方法也需要async
,然后 await
ed。在调用堆栈中重复此操作。
首先我要说我今天才开始尝试使用 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
,当我们得到整行(用换行符表示)时只有 returns 数据:
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()
,异步有类似的东西吗?
编辑: 澄清一下,这段代码确实有效,但我想知道这是否是 "proper" 做我正在做的事情的方式。
您需要更改:
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.
一般提示是"async all the way",这意味着任何await
是async
方法的方法也需要async
,然后 await
ed。在调用堆栈中重复此操作。