如何从同步创建异步方法

本文关键字:创建 异步方法 同步 | 更新日期: 2023-09-27 18:08:13

(c#代码如下)

我刚开始学习Async和Await。我查阅了一些文章和教程,我以为我掌握了它背后的概念,但在实现一些实际示例时,我似乎碰壁了。

这是我的UI类的New():
Public Sub New()
    ' This call is required by the designer.
    InitializeComponent()
    ' Add any initialization after the InitializeComponent() call.
    PopulateUI()
End Sub

…这里的关键是PopulateUI()。我想在不阻塞UI的情况下使用这个函数

编译器可以接受的唯一选项是:

Private Async Sub PopulateUI()
    Await Task.Run(Sub()
            ' Do some stuff that populates the UI comboboxes
        End Sub)
End Sub

…这个选项不起作用,我认为这是因为任务。Run运行在不同的线程中,所以更新组合框时会发生奇怪的事情(抱歉对描述如此模糊,我真的不知道更好)。

所以我发现了一个类似的问题,没有任何令人满意的答案,这让我觉得不是那么简单。希望我错了。


c#版本:

public MyUI()
{
    // This call is required by the designer.
    InitializeComponent();
    // Add any initialization after the InitializeComponent() call.
    PopulateUI();
}
Private async void PopulateUI()
{
    await Task.Run(() => 
    {
        // Do some stuff that populates the UI comboboxes
    })
}

如何从同步创建异步方法

db调用应该是可等待的,但是在这个时间点上,更改对我来说是相当昂贵的…那么,对

操作执行Task.Run()是不是一个糟糕的主意呢?

如您所述,理想的解决方案是使其始终是异步的。因此,下面的解决方案是一个hack(为了避免太多的代码更改),而不是最佳实践。

也就是说,你可以在这里使用async voidTask.Run;你只需要小心你的异常处理:
private async void PopulateUI()
{
  ... // Load initial view - "Loading..." message or whatever.
  try
  {
    var data = await Task.Run(() => 
    {
      ... // Read data from database.
    });
    ... // Update UI with data.
  }
  catch (Exception ex) // Not a typo. Catch all exceptions.
  {
    ... // Handle error - display message to user or whatever.
  }
}

首先:要非常小心async void ...:您将不会看到这样一个调用的任何异常。

我不知道这是否是规范模式(如果有这样的事情),但我通常做的是创建一个方法void CheckResult(Task task),它使用ContinueWith来附加必要的错误处理(通常检查任务是否有故障并记录/显示错误对话框)。

然后我要这样做:

public MyUI()
{
    // This call is required by the designer.
    InitializeComponent();
    // Add any initialization after the InitializeComponent() call.
    CheckResult(PopulateUI());
}
private async Task PopulateUI()
{
    var data = await FetchUiData();
    SetControlValues(data); // or whatever
}
private static void CheckResult(Task t) {
    // We want to be doing this on the UI thread
    var sched = TaskScheduler.FromCurrentSynchronizationContext();
    t.ContinueWith(() => {
       // check if "t" faulted and perform appropriate action
    }, sched);
}

在访问UI控件时使用Dispatcher,特别是在设置UI控件的属性时,奇怪的事情可能会消失。从Task中的任何位置获取数据。运行,但在UI控件中设置属性的代码应该使用dispatcher访问。