使用任务进行连续轮询
Continuous polling using Tasks
这是我一直使用 Threads / BackgroundWorker 的原因,但我正在尝试迁移到 Task 做事方式。
假设我有一个用于从 USB 端口读取字节的第 3 方 SDK。如果没有读取任何字节,该读取调用将阻塞并在 100 毫秒后超时,返回 null。如果读取字节,它会立即returns,返回读取字节的byte[]数组。
所以我基本上需要一遍又一遍地轮询,并通过调用 Parsing 函数对接收到的字节采取行动。这是一个 WPF 应用程序,因此返回的字节应该能够传递给 UI 线程函数。
执行此操作的正确方法是什么?这是我目前所拥有的,它似乎有效,但我想确保它是使用 TPL 做事的正确方法:
private void _connectUsbButton_Click(object sender, RoutedEventArgs e)
{
ListenForUsbMessagesAsync();
}
private async void ListenForUsbMessagesAsync()
{
while (true)
{
byte[] readBytes = await ReadBytesAsync();
Parse(readBytes);
}
}
private Task<byte[]> ReadBytesAsync()
{
Task<byte[]> readBytesTask = Task.Run(() =>
{
byte[] bytes;
do
{
bytes = ReadBytes();
} while (bytes == null);
return bytes;
});
return readBytesTask;
}
private byte[] ReadBytes()
{
byte[] readBytes = _usbSdk.ReadBytes(); //100ms timeout (returns null if no bytes read)
return readBytes;
}
我觉得还不错,这里只有一些建议:
private async Task ListenForUsbMessagesAsync(CancellationToken token)
{
while (true)
{
byte[] readBytes = await ReadBytesAsync();
Parse(readBytes);
token.ThrowIfCancellationRequested();
}
}
在其他地方,比如在 WPF 中 Window .ctor 存储这个
var tokenSource = new System.Threading.CancellationTokenSource();
最后像这样调用你的函数
private void _connectUsbButton_Click(object sender, RoutedEventArgs e)
{
ListenForUsbMessagesAsync(tokenSource.Token);
}
这样您就可以随时调用
取消您的任务
tokenSource.Cancel()
或者,如果您不想使用任务,您可以生成一个新线程并传入 Dispatcher 对象。通过这种方式,新创建的线程可以安全地将内容发送到 UI 线程。
由于您的轮询任务可能 运行 很长时间,您应该考虑 运行 在专用线程中进行它。
您可以通过在创建轮询任务时传递 TaskCreationOptions.LongRunning 标志来实现此目的。
像这样:
Task<byte[]> readBytesTask = Task.Factory.StartNew(() =>
{
byte[] bytes;
do
{
bytes = ReadBytes();
} while (bytes == null);
return bytes;
}, TaskCreationOptions.LongRunning);
这是我一直使用 Threads / BackgroundWorker 的原因,但我正在尝试迁移到 Task 做事方式。
假设我有一个用于从 USB 端口读取字节的第 3 方 SDK。如果没有读取任何字节,该读取调用将阻塞并在 100 毫秒后超时,返回 null。如果读取字节,它会立即returns,返回读取字节的byte[]数组。
所以我基本上需要一遍又一遍地轮询,并通过调用 Parsing 函数对接收到的字节采取行动。这是一个 WPF 应用程序,因此返回的字节应该能够传递给 UI 线程函数。
执行此操作的正确方法是什么?这是我目前所拥有的,它似乎有效,但我想确保它是使用 TPL 做事的正确方法:
private void _connectUsbButton_Click(object sender, RoutedEventArgs e)
{
ListenForUsbMessagesAsync();
}
private async void ListenForUsbMessagesAsync()
{
while (true)
{
byte[] readBytes = await ReadBytesAsync();
Parse(readBytes);
}
}
private Task<byte[]> ReadBytesAsync()
{
Task<byte[]> readBytesTask = Task.Run(() =>
{
byte[] bytes;
do
{
bytes = ReadBytes();
} while (bytes == null);
return bytes;
});
return readBytesTask;
}
private byte[] ReadBytes()
{
byte[] readBytes = _usbSdk.ReadBytes(); //100ms timeout (returns null if no bytes read)
return readBytes;
}
我觉得还不错,这里只有一些建议:
private async Task ListenForUsbMessagesAsync(CancellationToken token)
{
while (true)
{
byte[] readBytes = await ReadBytesAsync();
Parse(readBytes);
token.ThrowIfCancellationRequested();
}
}
在其他地方,比如在 WPF 中 Window .ctor 存储这个
var tokenSource = new System.Threading.CancellationTokenSource();
最后像这样调用你的函数
private void _connectUsbButton_Click(object sender, RoutedEventArgs e)
{
ListenForUsbMessagesAsync(tokenSource.Token);
}
这样您就可以随时调用
取消您的任务tokenSource.Cancel()
或者,如果您不想使用任务,您可以生成一个新线程并传入 Dispatcher 对象。通过这种方式,新创建的线程可以安全地将内容发送到 UI 线程。
由于您的轮询任务可能 运行 很长时间,您应该考虑 运行 在专用线程中进行它。
您可以通过在创建轮询任务时传递 TaskCreationOptions.LongRunning 标志来实现此目的。
像这样:
Task<byte[]> readBytesTask = Task.Factory.StartNew(() =>
{
byte[] bytes;
do
{
bytes = ReadBytes();
} while (bytes == null);
return bytes;
}, TaskCreationOptions.LongRunning);