【问题标题】:Windows Metro loading data asynchronouslyWindows Metro 异步加载数据
【发布时间】:2012-07-18 17:42:44
【问题描述】:

我创建了一个基于拆分页面示例应用程序的 Windows 8 Metro 应用程序。但是,在示例应用程序中,数据是在构造函数中同步加载的。我正在访问一个文本文件,因此需要异步加载数据。构造函数如下所示:

    public MyDataSource()
    {
        DataLoaded = false;

        LoadData();
    }

LoadData() 是一种填充数据模型的异步方法。这工作正常,并按加载它的方式显示数据(这是我想要的行为)。当我尝试测试挂起和终止时会出现问题。问题是恢复有可能在填充数据模型之前尝试访问它:

    public static MyDataGroup GetGroup(string uniqueId)
    {
        // If the data hasn't been loaded yet then what?
        if (_myDataSource == null)
        {
        // Where app has been suspended and terminated there is no data available yet
        }

        // Simple linear search is acceptable for small data sets
        var matches = _myDataSource.AllGroups.Where((group) => group.UniqueId.Equals(uniqueId));
        if (matches.Count() == 1) return matches.First();
        return null;
    }

我可以通过将构造函数更改为调用LoadData().Wait 来解决此问题,但这意味着应用程序会锁定 UI 线程。我相信我需要的是一种在GetGroup 中获取恢复代码的方法,以等待数据加载而不锁定 UI 线程。这是可能的还是可取的,如果可以,如何做?

编辑:

一两个人建议为LoadData() 缓存任务。这是个好主意,但GetGroup 中的代码由页面状态管理部分调用,因此不能异步。为了解决这个问题,我尝试了以下方法:

if (!DataLoaded)
{
    //dataLoading = await MyDataSource.LoadData();
    dataLoading.RunSynchronously();
}

但这给了我一个错误:

RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.

dataLoading.Wait()

只是锁定 UI。

【问题讨论】:

  • 为什么不在另一个线程中启动 LoadData() 呢?我不知道LoadData() 是否是自定义方法,如果您编写了它的代码,我高度怀疑这就是问题所在。
  • 在调用 LoadData 之前设置一个布尔值; IsLoading = true;当异步加载完成时设置 IsLoading = false;在你 GetGroup 检查 IsLoading == true;如果是这样,请等待。
  • @Blam - 这是我的想法。但是如何在不锁定 UI 线程的情况下等待呢?
  • @pm_2:如果您可以获取/存储可等待对象,那么您可以让 await 为您执行此操作。它让 GetGroup 的代码无论加载是否完成都可以工作
  • 与您的问题无关,但是如果您找到多个匹配项,您真的要返回null 吗?我认为例外会更有意义,在这种情况下,您可以使用 SingleOrDefault() 来简化代码。

标签: c# microsoft-metro windows-runtime .net-4.5 async-await


【解决方案1】:

我认为这听起来是最好的选择,如果您创建了构造函数async。但是由于that's not possible,你可以做的是为MyDataSource创建一个async工厂方法:

private MyDataSource()
{
    DataLoaded = false;
}

public static async Task<MyDataSource> Create()
{
    var source = new MyDataSource();
    await source.LoadData();
    return source;
}

然后使用await初始化_myDataSource

_myDataSource = await MyDataSource.Create();

如果由于某种原因你不能这样做,你可以将工厂方法返回的Task存储起来,并在GetGroup()中等待它:

_myDataSourceTask = MyDataSource.Create();

…

public static async Task<MyDataGroup> GetGroup(string uniqueId)
{
    var myDataSource = await _myDataSourceTask;

    // Simple linear search is acceptable for small data sets
    var matches = myDataSource.AllGroups.Where(group => group.UniqueId == uniqueId);
    if (matches.Count() == 1) return matches.First();
    return null;
}

【讨论】:

  • 不幸的是,这在实践中不起作用。原因是GetGroup 是由恢复应用程序状态的函数(LoadState)调用的,因此不能异步(GetGroup 不能异步)。
  • 您仍然可以从非异步方法启动异步操作。你不会等到它完成(除非你想阻止)。
  • 查看上面的编辑 - 显然在这个例子中,如果我不等待它完成,那么我最终会遇到与我开始时相同的情况
  • 好吧,如果你不能异步等待并且不想同步等待,那么这意味着你最终会得到未完成的Task。我对 WinRT 的了解还不够,无法进一步帮助您。
  • 对不起,我的最后一条评论完全错误(如果我可以投反对票,那么我会投反对票)。我的错误印象是不可能用异步覆盖同步方法 - 但它是并且现在可以正常工作 - 谢谢
【解决方案2】:

如果 LoadData 是异步的,则存储任何可等待对象(或创建一个新对象)并公开它(例如,像任务一样)然后 GetGroup 可以标记为异步并且可以执行 var data = await _myDataSource.LoadTask 或其他操作

【讨论】:

    【解决方案3】:

    我认为 Svick 最接近于回答这个问题,因为他使用了 Tasks。无论是在 GetGroup 方法上返回 Task 还是在 LoadAsync 方法上返回任务都可能无关紧要。重要的是您捕获该任务并稍后在简历中引用它。

    Task 类记录在 here,您会注意到它具有 IsCanceledIsCompletedIsFaulted 等属性。当您的构造函数启动 LoadAsync 方法时,您可以将它返回的 Task 保存为类级变量。稍后,当您的恢复代码启动时,您可以检查任务是否仍在运行(即未完成且未出现故障)。如果任务已完成,您可以立即运行您的简历代码。如果尚未完成,您可以使用 Task.ContinueWith 安排您的简历代码在任务完成时运行。

    【讨论】:

      【解决方案4】:

      我还没有检查 Windows 8,但我认为它的工作原理类似于 Windows Phone,假设您使用 Silverlight(或 WPF)来开发您的应用程序,我从您的问题中不清楚这一点。如果您使用 Silverlight:

      您需要使用 INotifyPropertyChanged 接口和 ObservableCollection-s 才能将数据绑定到 UI。这样,每次更改数据时,UI 都会收到有关更改的通知,并且绑定会刷新。

      【讨论】:

      • 绑定工作正常。如问题所述,此问题与暂停和终止后的事件顺序有关。
      【解决方案5】:

      MyDataGroup 一个实现 iNotifyPropertyChanged 的​​公共属性。

      UniqueID 一个公共属性。 当 UniqueID 更改设置 MyDataGroup = null 然后调用 BackGroundWorker 到 var mathces = 但延迟,如果 LoadData();正在工作中。由于这是在后台,延迟不会占用 UI。在回调设置 MyDataGroup 和 UI 将得到通知。确保您的 backgroundworker 是可取消的,因为您希望在 UniqueID 更改时取消它(如果它正在运行)。

      我在 WPF 中执行此操作,因此如果它不能转换为 Metro 很抱歉,我将删除。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-01-03
        • 2020-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-20
        相关资源
        最近更新 更多