如何在不阻塞 UI 线程的情况下继续执行多个任务?

2024-01-31

在我的 MVVM 应用程序中,我的视图模型调用 3 个不同的服务方法,将每个方法的数据转换为通用格式,然后使用属性通知/可观察集合等更新 UI。

服务层的每个方法都会启动一个新的Task并返回Task到视图模型。这是我的一种服务方法的示例。

public class ResourceService
{
internal static Task LoadResources(Action<IEnumerable<Resource>> completedCallback, Action<Exception> errorCallback)
{
    var t = Task.Factory.StartNew(() =>
    {
        //... get resources from somewhere
        return resources;
    });

    t.ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            errorCallback(task.Exception);
            return;
        }
        completedCallback(task.Result);
    }, TaskScheduler.FromCurrentSynchronizationContext());

    return t;
}
}

这是视图模型的调用代码和其他相关部分......

private ObservableCollection<DataItem> Data = new ObservableCollection<DataItem>();

public ICollectionView DataView
{
    get { return _dataView; }
    set
    {
        if (_dataView != value)
        {
            _dataView = value;
            RaisePropertyChange(() => DataView);
        }
    }
}

private void LoadData()
{
    SetBusy("Loading...");

    Data.Clear();

    Task[] tasks = new Task[3]
    {
        LoadTools(),
        LoadResources(),
        LoadPersonel()
    };

    Task.WaitAll(tasks);

    DataView = CollectionViewSource.GetDefaultView(Data);
    DataView.Filter = FilterTimelineData;

    IsBusy = false;
}

private Task LoadResources()
{
    return ResourceService.LoadResources(resources =>
    {
        foreach(var r in resources)
        {
            var d = convertResource(r);
            Data.Add(d);
        }
    },
    error => 
    {
        // do some error handling
    });
}

这几乎可以工作,但有一些小问题。

第 1 号:在呼叫中SetBusy在一开始,在我开始任何任务之前以及在我打电话之前WaitAll,我设置了IsBusy属性为真。这应该会更新 UI 并显示 BusyIndi​​cator 控件,但它不起作用。我还尝试添加简单的字符串属性并绑定它们,但它们也没有被更新。 IsBusy 功能是基类的一部分,适用于其他视图模型,在这些模型中我没有运行多个任务,因此我不认为 XAML 中的属性通知或数据绑定存在问题。

所有数据绑定似乎在整个方法完成后都已更新。我在输出窗口中没有看到任何“首次异常”或绑定错误,这使我相信 UI 线程在调用 WaitAll 之前以某种方式被阻止。

第二:我似乎从服务方法返回了错误的任务。我想要之后的一切WaitAll在视图模型转换回调中所有服务方法的所有结果后运行。但是,如果我从服务方法返回延续任务,则永远不会调用延续任务,并且WaitAll永远等待。奇怪的是,绑定到 ICollectionView 的 UI 控件实际上正确显示了所有内容,我认为这是因为 Data 是一个可观察的集合,并且 CollectionViewSource 知道集合更改事件。


您可以使用TaskFactory.ContinueWhenAll http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory.continuewhenall%28v=vs.100%29.aspx构建一个在输入任务全部完成时运行的延续。

Task[] tasks = new Task[3]
{
    LoadTools(),
    LoadResources(),
    LoadPersonel()
};

Task.Factory.ContinueWhenAll(tasks, t =>
{
    DataView = CollectionViewSource.GetDefaultView(Data);
    DataView.Filter = FilterTimelineData;

    IsBusy = false;
}, CancellationToken.None, TaskContinuationOptions.None, 
   TaskScheduler.FromCurrentSynchronizationContext());

请注意,如果您使用 C# 5,这会变得更简单await/async syntax:

private async void LoadData()
{
    SetBusy("Loading...");

    Data.Clear();

    Task[] tasks = new Task[3]
    {
        LoadTools(),
        LoadResources(),
        LoadPersonel()
    };

    await Task.WhenAll(tasks);

    DataView = CollectionViewSource.GetDefaultView(Data);
    DataView.Filter = FilterTimelineData;

    IsBusy = false;
}

但是,如果我从服务方法返回延续任务,则永远不会调用延续任务,并且 WaitAll 会永远等待

问题是您的延续任务需要 UI 线程,并且您正在阻塞 UI 线程WaitAll称呼。这会造成一个无法解决的僵局。

修复上述问题应该可以纠正这个问题 - 您需要将延续作为任务返回,因为这就是您需要等待完成的内容 - 但是通过使用TaskFactory.ContinueWhenAll您释放 UI 线程,以便它可以处理这些延续。

请注意,这是 C# 5 简化的另一件事。您可以将其他方法编写为:

internal static async Task LoadResources(Action<IEnumerable<Resource>> completedCallback, Action<Exception> errorCallback)
{
  try
  {
    await Task.Run(() =>
    {
        //... get resources from somewhere
        return resources;
    });
  }
  catch (Exception e)
  {
    errorCallback(task.Exception);
  }

  completedCallback(task.Result);
}

话虽这么说,通常最好编写返回一个的方法Task<T>而不是提供回调,因为这简化了两端的使用。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在不阻塞 UI 线程的情况下继续执行多个任务? 的相关文章

  • 在 C++ 中使用 matlab 结构(matlab 函数调用的返回值)(由 matlab 编译器生成的库)

    你好 我有一个相当简单的 matlab 函数 例如 function MYSTRUCT myfunc MYSTRUCT prop1 test MYSTRUCT prop2 foo MYSTRUCT prop3 42 end 我用 matla
  • 如何在c++中读取pcap文件来获取数据包信息?

    我想用 C 编写一个程序来读取 pcap 文件并获取数据包的信息 例如 len sourc ip flags 等 现在我找到了如下代码 我认为它会帮助我获取信息 但是我有一些疑问 首先我想知道应该将哪个库添加到我的程序中 然后什么是 pca
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • C中的malloc内存分配方案

    我在 C 中尝试使用 malloc 发现 malloc 在分配了一些内存后浪费了一些空间 下面是我用来测试 malloc 的一段代码 include
  • 如何区分用户点击链接和页面自动重定向?

    拥有 C WebBrowser control http msdn microsoft com en us library system windows forms webbrowser aspx在我的 WinForms 应用程序中 并意识
  • 将 Word 文档另存为图像

    我正在使用下面的代码将 Word 文档转换为图像文件 但是图片显得太大 内容不适合 有没有办法渲染图片或将图片保存到合适的尺寸 private void btnConvert Click object sender EventArgs e
  • 在 Visual Studio 2010 中从 Fortran 调用 C++ 函数

    我想从 Fortran 调用 C 函数 为此 我在 Visual Studio 2010 中创建了一个 FORTRAN 项目 之后 我将一个 Cpp 项目添加到该 FORTRAN 项目中 当我要构建程序时出现以下错误 Error 1 unr
  • qdbusxml2cpp 未知类型

    在使用 qdbusxml2cpp 程序将以下 xml 转换为 Qt 类时 我收到此错误 qdbusxml2cpp c ObjectManager a ObjectManager ObjectManager cpp xml object ma
  • 为什么调用非 const 成员函数而不是 const 成员函数?

    为了我的目的 我尝试包装一些类似于 Qt 共享数据指针的东西 经过测试 我发现当应该调用 const 函数时 会选择它的非 const 版本 我正在使用 C 0x 选项进行编译 这是一个最小的代码 struct Data int x con
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的
  • C#:帮助理解 UML 类图中的 <>

    我目前正在做一个项目 我们必须从 UML 图编写代码 我了解 UML 类图的剖析 但我无法理解什么 lt
  • 等待进程释放文件

    我如何等待文件空闲以便ss Save 可以用新的覆盖它吗 如果我紧密地运行两次 左右 我会得到一个generic GDI error
  • 动态添加 ASP.Net 控件

    我有一个存储过程 它根据数据库中存储的记录数返回多行 现在我想有一种方法来创建 div 带有包含该行值的控件的标记 如果从数据库返回 10 行 则 10 div 必须创建标签 我有下面的代码来从数据库中获取结果 但我不知道如何从这里继续 S
  • 使用 %d 打印 unsigned long long

    为什么我打印以下内容时得到 1 unsigned long long int largestIntegerInC 18446744073709551615LL printf largestIntegerInC d n largestInte
  • 使用 C# 读取 Soap 消息

  • 按 Esc 按键关闭 Ajax Modal 弹出窗口

    我已经使用 Ajax 显示了一个面板弹出窗口 我要做的是当用户按 Esc 键时关闭该窗口 这可能吗 如果有人知道这一点或以前做过这一点 请帮助我 Thanks 通过以下链接 您可以通过按退出按钮轻松关闭窗口 http www codepro
  • 调用堆栈中的“外部代码”是什么意思?

    我在 Visual Studio 中调用一个方法 并尝试通过检查调用堆栈来调试它 其中一些行标记为 外部代码 这到底是什么意思 方法来自 dll已被处决 外部代码 意味着该dll没有可用的调试信息 你能做的就是在Call Stack窗口中单
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • 如何从 ODBC 连接获取可用表的列表?

    在 Excel 中 我可以转到 数据 gt 导入外部数据 gt 导入数据 然后选择要使用的数据源 然后在提供登录信息后 它会给我一个表格列表 我想知道如何使用 C 以编程方式获取该列表 您正在查询什么类型的数据源 SQL 服务器 使用权 看
  • 当从finally中抛出异常时,Catch块不会被评估

    出现这个问题的原因是之前在 NET 4 0 中运行的代码在 NET 4 5 中因未处理的异常而失败 部分原因是 try finallys 如果您想了解详细信息 请阅读更多内容微软连接 https connect microsoft com

随机推荐

  • 为什么 C#7 语法中的 TryParse(清空参数)在编译时会发出警告?

    在 C 7 中 你可以这样做 if int TryParse 123 out int result Console WriteLine Parsed result 或者 如果您不使用结果而只想检查解析是否成功 discard https l
  • 用于复制的 EBS 卷的快照

    我在 EBS 卷上设置了一个带有 MySQL 的 EC2 实例 并设置了另一个充当复制从属实例 复制设置很好 我的问题是关于拍摄这些卷的快照 我注意到快照过程需要锁定表 这可能会给用户带来不便 因此 我的想法是保留主实例并拍摄作为从实例的快
  • 从队列中获取最后 n 个项目

    我看到的一切都是关于列表的 但这是关于events queue queue 这是一个包含我想要提取的对象的队列 但是我如何从该队列中获取最后 N 个元素 根据定义 你不能 你可以做的是使用循环或理解get the first 你不能get从
  • 为什么body.scrollHeight自动增加而不减少

    我的 iframe body 有一个scrollHeight我认为是只读的属性 我不明白它是如何设置的 当我修改iframe body innerHTML和一些largerHTML 我注意到iframe body scrollHeight
  • 如何用随机字典值填充 pandas 数据框列

    我是 Pandas 新手 我想使用随机文本数据 我正在尝试向 DataFrame df 添加 2 个新列 每个列都由从字典中随机选择的键 newcol1 值 newcol2 填充 countries Africa Ghana Europe
  • TinyMCE中通过execCommand(insertContent)插入元素的参考

    我需要一个我通过 TinyMCE 编辑器插入的元素的参考 ed execCommand mceInsertContent false span class marker my node content span 或者 是否有任何解决方法可以
  • CollapsingToolbarLayout 以编程方式扩展动画持续时间

    我在 Android 的应用程序中使用 CollapsingToolbarLayout 我的应用程序的最低要求 API 是 9 我需要当用户单击折叠的工具栏时展开折叠的工具栏 就像在最新的 Gmail 日历应用程序中一样 所以我设置了一个
  • 多用户数据源 - Spring + Hibernate

    我正在编写一个支持多个用户的网络应用程序 每个用户都有自己的数据库 使用H2 所有数据库模式都是相同的 我希望在这个应用程序中使用 Spring Hibernate 所以我被困在如何将用户的数据库与该用户关联起来 也许在HTTPSessio
  • 可对动态内容进行排序

    所以我使用 jQuery UI 可排序插件对小图库中的照片进行排序 function area sortable items sort wrapper cursor move handle photo handler opacity 0 5
  • django:如何为 SuspiciousOperation 异常创建自定义日志过滤器?

    迁移到 1 11 从 1 8 后 我收到了一些SuspiciousOperation记录错误 它似乎来自 JS 请求 如果用户移动鼠标 它会保持会话活动 但这并不重要 我怎样才能过滤这个异常 我尝试过的 我刚刚在某处创建了一个过滤器 imp
  • 限制变量范围

    我正在尝试编写一个函数 它限制 R 变量的范围 例如 source LimitScope R y 0 f function Raises an error as y is a global variable x y 我想过测试可变环境 但不
  • 我应该如何更新“热门”表?

    我有一个带有 热 表的 postgres 数据库 这意味着它每秒都会收到许多更新 删除 我想运行一个查询UPDATE大约 10 的行 我没有 10 标准的索引 隔离级别设置为SERIALIZABLE作为数据库标志 影响所有事务 我希望这个查
  • Elixir Phoenix 生产服务器出现 Letscrypt 续订问题

    我有一个使用 Elixir Phoenix 框架构建的网站 该网站在开发和生产模式下都运行良好 当phoenix服务器在开发模式下运行时 我更新Let s Encrypt证书没有问题 但是当完全一样应用程序正在生产模式下运行 尝试更新时我不
  • 为什么Java的Area#equals方法不覆盖Object#equals?

    我刚刚遇到了一个由Java引起的问题java awt geom Area equals Area 方法 该问题可以简化为以下单元测试 org junit Test public void testEquals java awt geom A
  • Oracle 中数字的默认精度和小数位数是多少?

    在 Oracle 中创建 NUMBER 类型的列时 您可以选择不指定精度或小数位数 如果您不指定这些默认值 它们会做什么 数字 精度 小数位数 如果未指定精度 则该列将存储给定的值 如果未指定比例 则比例为零 更多信息请访问 http do
  • 检测iframe是否跨域的万无一失的方法

    我正在尝试确定是否有任何 iframe 是跨域的 根据这个问题中接受的答案 检测iframe何时跨域 然后退出它 https stackoverflow com questions 2365822 detect when iframe is
  • 在 C 中查找列表的基数

    我怎样才能只找到列表中出现一次的元素并返回基数 例如 如果我的列表由 3 2 1 1 2 4 组成 我期望返回计数器为 4 而不是6 因为我们不计算重复的数字 这是我到目前为止编写的代码 struct Node int data struc
  • 如何在 Android 中使用 LocationManager#getCurrentLocation

    我需要获取当前位置信息 目前 我正在使用获取最后的已知位置 https developer android com reference android location LocationManager getLastKnownLocatio
  • ASP.NET Core 中的通用存储库在 Startup.cs 中的每个表中没有单独的 AddScoped 行?

    我的项目中有一个通用存储库 考虑以下控制器片段 public class Lookup1Controller Controller readonly MyDbContext db public Lookup1Controller MyDbC
  • 如何在不阻塞 UI 线程的情况下继续执行多个任务?

    在我的 MVVM 应用程序中 我的视图模型调用 3 个不同的服务方法 将每个方法的数据转换为通用格式 然后使用属性通知 可观察集合等更新 UI 服务层的每个方法都会启动一个新的Task并返回Task到视图模型 这是我的一种服务方法的示例 p