为什么我的异步方法不等待"serialPort = await SerialDevice.FromIdAsync()"?



我正在尝试创建一个类,初始化树莓和arduino之间的串行连接。该类还应能够通过已建立的串行连接进行读写。

我使用的代码来自微软开发者网站。

using Windows.Storage.Streams;
using Windows.Devices.Enumeration;
using Windows.Devices.SerialCommunication;
public async void Serial()
{
/* Find the selector string for the serial device   */
string aqs = SerialDevice.GetDeviceSelector("UART0");
/* Find the serial device with our selector string  */
var dis = await DeviceInformation.FindAllAsync(aqs);
/* Create an serial device with our selected device */
SerialDevice SerialPort = await SerialDevice.FromIdAsync(dis[0].Id);
/* Configure serial settings */
SerialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
SerialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
SerialPort.BaudRate = 9600;
SerialPort.Parity = SerialParity.None;
SerialPort.StopBits = SerialStopBitCount.One;
SerialPort.DataBits = 8;
/* Write a string out over serial */
string txBuffer = "Hello Serial";
DataWriter dataWriter = new DataWriter();
dataWriter.WriteString(txBuffer);
uint bytesWritten = await SerialPort.OutputStream.WriteAsync(dataWriter.DetachBuffer());
/* Read data in from the serial port */
const uint maxReadLength = 1024;
DataReader dataReader = new DataReader(SerialPort.InputStream);
uint bytesToRead = await dataReader.LoadAsync(maxReadLength);
string rxBuffer = dataReader.ReadString(bytesToRead);
}

我在Package.appxmanifest.中添加了串行通信功能

<Capabilities>
<DeviceCapability Name="serialcommunication">
<Device Id="any">
<Function Type="name:serialPort" />
</Device>
</DeviceCapability>
</Capabilities>

到目前为止一切都很好。复盆子成功地发送和接收来自另一端Arduino的消息。

现在我试着分别做这些步骤。首先初始化串行连接,以便在应用程序中需要时可以使用读写功能。

主页

namespace SerialUART
{
public sealed partial class MainPage : Page
{
private SerialController serial = new SerialController();
public MainPage()
{
this.InitializeComponent();
serial.Write();
serial.Read();
}
}
}

串行控制器

using Windows.Devices.Enumeration;
using Windows.Devices.SerialCommunication;
using Windows.Storage.Streams;
namespace SerialUART
{
class SerialController
{
private SerialDevice SerialPort;
private DataWriter dataWriter;
private DataReader dataReader;
public SerialController()
{
InitSerial();
}
private async void InitSerial()
{
string aqs = SerialDevice.GetDeviceSelector("UART0");
var dis = await DeviceInformation.FindAllAsync(aqs);
SerialPort = await SerialDevice.FromIdAsync(dis[0].Id);    
// The program does not wait here and executes Write()
// after Write() the following code will be executed
SerialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
SerialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
SerialPort.BaudRate = 9600;
SerialPort.Parity = SerialParity.None;
SerialPort.StopBits = SerialStopBitCount.One;
SerialPort.DataBits = 8;
dataWriter = new DataWriter();
dataReader = new DataReader(SerialPort.InputStream);
}
public async void Read()
{
/* Read data in from the serial port */
const uint maxReadLength = 1024;
uint bytesToRead = await dataReader.LoadAsync(maxReadLength);
string rxBuffer = dataReader.ReadString(bytesToRead);
Debug.WriteLine(rxBuffer);
}
public async void Write()
{
/* Write a string out over serial */
string txBuffer = "Hello Serial";
dataWriter.WriteString(txBuffer);
uint bytesWritten = await SerialPort.OutputStream.WriteAsync(dataWriter.DetachBuffer());
}
}
}

此代码不等待serialPort、dataWriter和dataReader初始化。所以他们从来没有被分配到。

有人能向我解释一下为什么它不等待串行连接吗?应该怎么做?

所有的async方法都返回void-这意味着它们开始执行,并且调用代码没有简单的方法,要等到它真正完成后才能继续。一旦在尚未完成的事情上使用await表达式,异步方法就会返回给调用者,并计划了一个延续,该延续将在awaitable完成时执行。在你的情况下,你只是继续下一个陈述。

相反,您应该让异步方法返回Task,并更改调用方法,等待异步方法完成后再继续。这很难做到优雅地,因为在每种情况下,您都从构造函数中调用异步方法,而构造函数本身不可能是异步的。您可能需要考虑使用异步静态方法,而不是在构造函数中执行工作,构造函数可以调用异步方法,等待它们,然后调用一个不执行任何实际工作的实际构造函数。

不过,在进行任何详细的更改之前,您确实应该阅读更多关于asyncawait的内容——在不了解它们的情况下尝试使用它们会导致进一步的问题。

这是您的构造函数:

public SerialController()
{
InitSerial();
}

它调用InitSerial()方法,该方法中有几个等待。构造函数立即返回,而不执行所需的所有操作。然后你调用这些方法:

serial.Write();
serial.Read();

但是这些方法依赖于InitSerial方法中执行的每一行代码,但显然它们没有。

解决这个问题的一种方法是使InitSerial不是私有的(内部或公共的),并让它返回Task。当您调用它时,您需要等待它,以便它已被执行到完成。之后,您可以调用serial.Write()Serial.Read()

您应该避免async void。它们仅用于事件处理。对于其他方法,返回TaskTask<T>

最新更新