【发布时间】:2020-03-24 17:38:18
【问题描述】:
我正在开发固件更新应用程序,并尝试使用 Async/Await 来避免阻塞 UI。我面临的问题是更新固件的过程需要按顺序进行一系列步骤(这不是异步的)。然而,当涉及 UI 以避免冻结应用程序时,看起来异步仍然是要走的路。那么我们如何才能不阻塞 UI,顺序完成一系列长时间运行的任务,又不吃不饱也不浪费线程呢?
这里有一个非异步的例子来说明这个问题:
public class FirmwareUpdateViewModel : ObservableObject
{
//ctor
public FirmwareUpdateViewModel()
{
// Process should start on page load
updateFirmware();
}
private void updateFirmware()
{
//...
DeviceHandshake();
SendMessage(Commands.RestartDevice);
GetFWFileData();
WaitForDevice(timeout);
SendMessage(PreliminaryData);
//...
}
}
我的第一次尝试是await Task.Run(() =>一切:
public class FirmwareUpdateViewModel : ObservableObject
{
//ctor
public FirmwareUpdateViewModel()
{
updateFirmwareAsync();
}
private async void updateFirmwareAsync()
{
//...
await Task.Run(() => DeviceHandshakeAsync());
await Task.Run(() => SendMessageAsync(Commands.RestartDevice));
//...
}
}
除了async void 之外,这里的问题是由于异步的病毒性质,当我在DeviceHandshakeAsync 中等待时,加上如果Task.Run() 在它自己的线程上运行,那么我们就浪费了大量的线程。
接下来我尝试删除 Task.Run(() => 并等待每个方法,但据我所知,当子方法遇到内部 await 并开始在前一个之前运行下一个子方法时,它们将返回完成。现在我正在尝试异步运行firmwareUpdate方法,但不等待子方法以保持它们同步并对抗病毒性质,但这会阻塞UI:
public class FirmwareUpdateViewModel : ObservableObject
{
//ctor
public FirmwareUpdateViewModel()
{
Result = new NotifyTaskComplete<bool>(updateFirmwareAsync());
}
private async Task<bool> updateFirmwareAsync()
{
//...
DeviceHandshake();
SendMessage(Commands.RestartDevice);
//...
}
}
这里的问题显然是没有awaits,我们在UI线程上同步运行。对于我来说,在这里理解正确的方法还没有“点击”。我们如何正确地做到这一点?
编辑:我意识到我遗漏了大部分问题...... USB 由同步的第 3 方库处理。所以我需要以某种方式包装这些函数以避免阻塞 UI:
public class FirmwareUpdateViewModel : ObservableObject
{
//ctor
public FirmwareUpdateViewModel()
{
// Don't block the UI with this
updateFirmware();
}
private void updateFirmware()
{
//...
// Run all non-blocking but sequentially
DeviceHandshake();
SendMessage(Commands.RestartDevice);
GetFWFileData();
WaitForDevice(timeout);
SendMessage(PreliminaryData);
//...
}
private void SendMessage(Command)
{
// Existing code, can't make async
USBLibrary.Write(Command);
}
}
这就是我如此困惑的原因,因为 async 想要传播但我不能在构造函数和结束库之间。
【问题讨论】:
-
wasting a ton of threads不,会在线程池上执行,线程池是固定的一组线程 -
“据我所知,子方法会在遇到内部等待时返回并开始运行下一个子方法” - 没有。这就是你误会的核心。任何对 async/await 的基本介绍都应该清楚这一点。
-
@Renat 仍然 I/O 绑定的工作不应该包装在 CPU 绑定的任务中,只是为了使其异步。第一个选择应该是使 I/O 本身异步,例如通过通过 TaskCompletionSource 将 I/O 完成事件或完成回调包装到 I/O 绑定任务中。
-
是的,基本上就是这样。您在方法中创建一个 TaskCompletionSource
tcs。你在方法中调用的 I/O 方法必须有某种完成事件或回调,当这个事件被触发时,你调用tcs.TrySetResult。你的方法只返回tcs.Task。结果是您的方法仅启动 I/O 然后退出,当您等待您的方法时,在调用 TrySetResult 后,等待之后的所有内容都将异步执行。
标签: c# multithreading asynchronous async-await