WPF 异步

news2024/9/23 9:35:40

在 WPF 中,异步编程非常重要,尤其是为了保持 UI 线程的响应性。由于 WPF 的 UI 操作必须在主线程上进行,耗时的任务(如文件读写、网络请求等)如果直接在 UI 线程上执行,会导致 UI 冻结,界面无法响应用户操作。因此,使用异步编程可以避免这些问题,使得任务能够在后台线程中执行,同时保持 UI 流畅。

1. 异步编程的基本概念

异步编程可以通过以下几种方式实现:

  1. async/await 关键词:这是最常见的异步编程方式,能够让耗时操作在后台执行,同时保持代码的可读性和清晰度。
  2. Task:异步操作通常会返回一个 Task,用来表示操作的状态和结果。
  3. Dispatcher:由于 WPF 的 UI 操作只能在主线程上完成,当后台任务执行完毕后,需要使用 Dispatcher 回到 UI 线程更新 UI。

2. 使用 async/await 进行异步操作

asyncawait 是 .NET 中处理异步操作的核心关键词。通过这两个关键词,可以让异步任务在后台运行,而不阻塞主线程。

示例:通过 async/await 读取文件并在读取完成后更新 UI。

private async void ReadFileButton_Click(object sender, RoutedEventArgs e)
{
    string filePath = "path_to_file.txt";
    
    // 异步读取文件内容
    string fileContent = await ReadFileAsync(filePath);
    
    // 更新 UI
    FileContentTextBox.Text = fileContent;
}

private async Task<string> ReadFileAsync(string filePath)
{
    using (StreamReader reader = new StreamReader(filePath))
    {
        return await reader.ReadToEndAsync();
    }
}

解释

  • await 关键字在后台执行文件读取操作,UI 线程不会被阻塞。
  • 任务完成后,返回文件内容并更新 TextBox

3. 处理异步任务中的异常

在异步编程中,异常处理和同步代码略有不同。通常,异步任务中的异常需要在调用 await 时捕获。

示例

private async void LoadDataAsync()
{
    try
    {
        await Task.Run(() =>
        {
            // 模拟一个异常
            throw new InvalidOperationException("Something went wrong");
        });
    }
    catch (Exception ex)
    {
        // 异常处理逻辑
        MessageBox.Show($"Error: {ex.Message}");
    }
}

解释

  • 异常会在 await 处抛出,因此异常处理需要在异步方法调用的地方进行捕获。

4. 避免 UI 冻结的常见异步操作

异步操作通常用于以下场景:

  • 文件操作:文件的读写操作可以在后台执行,避免阻塞 UI。
  • 网络请求:通过异步调用外部 API 或下载数据,可以使 UI 保持响应。
  • 数据库查询:长时间的数据库查询可以通过异步执行,避免界面卡顿。
  • 计算密集型任务:如大量数据处理或复杂算法,可以通过异步方式放到后台执行。

示例:异步网络请求

private async void DownloadButton_Click(object sender, RoutedEventArgs e)
{
    string url = "https://example.com/data";
    
    // 异步下载数据
    string result = await DownloadDataAsync(url);
    
    // 更新 UI
    ResultTextBox.Text = result;
}

private async Task<string> DownloadDataAsync(string url)
{
    using (HttpClient client = new HttpClient())
    {
        return await client.GetStringAsync(url);
    }
}

5. 在异步任务中更新 UI

WPF 的 UI 元素必须在 UI 线程中更新,无法直接从后台线程操作 UI。为了在异步任务完成后更新 UI,需要切换回 UI 线程。Dispatcher 类提供了这种机制。

示例:在后台任务完成后使用 Dispatcher 更新 UI。

private async void LongRunningTask_Click(object sender, RoutedEventArgs e)
{
    await Task.Run(() =>
    {
        // 模拟耗时任务
        Thread.Sleep(3000);
        
        // 回到UI线程
        Application.Current.Dispatcher.Invoke(() =>
        {
            StatusLabel.Content = "Task Completed!";
        });
    });
}

解释

  • 在异步任务中,通过 Dispatcher.Invoke 切换回 UI 线程,确保可以安全地操作 UI 控件。

6. 使用 Task.Run 执行后台任务

有时,我们可能需要将一个计算密集型或耗时的操作放到后台线程运行。Task.Run 是一种常见的方式,将任务放到线程池中执行。

示例

private async void ComputeTask_Click(object sender, RoutedEventArgs e)
{
    int result = await Task.Run(() => PerformLongCalculation());
    
    ResultLabel.Content = $"Calculation Result: {result}";
}

private int PerformLongCalculation()
{
    // 模拟长时间计算
    Thread.Sleep(2000);
    return 42;
}

7. Dispatcher.InvokeDispatcher.BeginInvoke 的区别

在使用 Dispatcher 时,有两种调用方法:

  • Dispatcher.Invoke:同步调用,会阻塞当前线程,直到操作完成。
  • Dispatcher.BeginInvoke:异步调用,立即返回,不会阻塞当前线程。

通常在异步操作中推荐使用 Dispatcher.BeginInvoke 来避免阻塞主线程。

示例

private async void UpdateUITask_Click(object sender, RoutedEventArgs e)
{
    await Task.Run(() =>
    {
        // 模拟后台任务
        Thread.Sleep(3000);
        
        // 使用 BeginInvoke 回到 UI 线程
        Application.Current.Dispatcher.BeginInvoke(new Action(() =>
        {
            StatusLabel.Content = "Task Completed!";
        }));
    });
}

8. CancellationToken 实现任务取消

在某些场景下,用户可能希望能够取消正在执行的异步任务。CancellationToken 提供了一种机制,允许在异步操作中检查是否需要取消任务。

示例

private CancellationTokenSource _cts;

private async void StartCancellableTask_Click(object sender, RoutedEventArgs e)
{
    _cts = new CancellationTokenSource();
    try
    {
        await Task.Run(() => LongRunningOperation(_cts.Token), _cts.Token);
        StatusLabel.Content = "Operation Completed";
    }
    catch (OperationCanceledException)
    {
        StatusLabel.Content = "Operation Canceled";
    }
}

private void LongRunningOperation(CancellationToken token)
{
    for (int i = 0; i < 10; i++)
    {
        // 检查任务是否取消
        token.ThrowIfCancellationRequested();
        Thread.Sleep(1000); // 模拟长时间操作
    }
}

private void CancelTask_Click(object sender, RoutedEventArgs e)
{
    _cts.Cancel();
}

解释

  • 通过 CancellationToken 来检查任务是否已经被取消,并通过 ThrowIfCancellationRequested 抛出异常以终止任务。

9. Progress<T> 实现任务进度更新

在异步任务执行时,有时需要将任务进度反馈给用户。可以使用 IProgress<T> 接口来实现进度报告。

示例

private async void StartProgressTask_Click(object sender, RoutedEventArgs e)
{
    var progress = new Progress<int>(percent =>
    {
        ProgressBar.Value = percent;
    });

    await Task.Run(() => LongRunningTaskWithProgress(progress));
}

private void LongRunningTaskWithProgress(IProgress<int> progress)
{
    for (int i = 0; i <= 100; i += 10)
    {
        // 报告进度
        progress.Report(i);
        Thread.Sleep(500); // 模拟长时间操作
    }
}

解释

  • Progress<T> 接口用于异步任务中向 UI 线程报告任务的进度,并在 UI 上实时显示。

总结:

  • WPF 中的异步操作通过 async/awaitTask 类实现,能够防止 UI 冻结,提升用户体验。
  • 异步任务中的 UI 更新需要通过 Dispatcher 切换到 UI 线程。
  • CancellationTokenProgress<T> 分别提供了任务取消和进度报告的支持。
  • 使用异步编程可以更高效地处理 I/O 密集型任务和计算密集型任务,同时保持 UI 的响应性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2157204.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一劳永逸:用脚本实现夸克网盘内容自动更新

系统环境&#xff1a;debian/ubuntu 、 安装了python3 原作者项目&#xff1a;https://github.com/Cp0204/quark-auto-save 感谢 缘起 我喜欢看电影追剧&#xff0c;会经常转存一些资源到夸克网盘&#xff0c;电影还好&#xff0c;如果是电视剧&#xff0c;麻烦就来了。 对于一…

Kettle的安装及简单使用

Kettle的安装及简单使用一、kettle概述二、kettle安装部署和使用Windows下安装案例1&#xff1a;MySQL to MySQL案例2&#xff1a;使用作业执行上述转换&#xff0c;并且额外在表stu2中添加一条数据案例3&#xff1a;将hive表的数据输出到hdfs案例4&#xff1a;读取hdfs文件并将…

Jboss常⻅中间件漏洞

一.CVE-2015-7501 环境搭建 cd vulhub-master/jboss/JMXInvokerServlet-deserialization docker-compose up -d 1.POC&#xff0c;访问地址 172.16.1.4:8080/invoker/JMXInvokerServlet 返回如下&#xff0c;说明接⼝开放&#xff0c;此接⼝存在反序列化漏洞 2.下载 ysose…

7.C++程序中的基本数据类型-数据类型之间的转换

在C中&#xff0c;类型转换是将一个数据类型转为另外一个数据类型&#xff0c;其转换过程比较复杂&#xff0c;目前只讨论基本数据类型之间的转换。 类型转换分为两部分&#xff1a;隐式转换和显示转换 隐式转换又称为自动转换&#xff0c;显示转换又称为强制转换。 隐式转换…

[Linux] Linux进程PCB内部信息的深入理解

标题&#xff1a;[Linux] Linux进程PCB内部信息的深入理解 个人主页&#xff1a;水墨不写bug &#xff08;图片来自网络&#xff09; 目录 一.查看进程 二.认识并了解进程的关键信息 I&#xff0c;PID/PPID II&#xff0c;exe III&#xff0c;cwd 三、fork&#xff08;&…

vue源码分析(九)—— 合并配置

文章目录 前言1.vue cli 创建一个基本的vue2 项目2.将mian.js文件改成如下3. 运行结果及其疑问&#xff1f; 一、使用 new Vue 创建过程的 2 种场景二、margeOption的详细说明1.margeOption的方法地址2.合并策略的具体使用3.defaultStrat 默认策略方法 三&#xff1a;以生命周期…

OpenResty安装及使用

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

调用本地大模型服务出现PermissionDeniedError的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

【机器学习】---神经架构搜索(NAS)

这里写目录标题 引言1. 什么是神经架构搜索&#xff08;NAS&#xff09;1.1 为什么需要NAS&#xff1f; 2. NAS的三大组件2.1 搜索空间搜索空间设计的考虑因素&#xff1a; 2.2 搜索策略2.3 性能估计 3. NAS的主要方法3.1 基于强化学习的NAS3.2 基于进化算法的NAS3.3 基于梯度的…

ICM20948 DMP代码详解(38)

接前一篇文章&#xff1a;ICM20948 DMP代码详解&#xff08;37&#xff09; 上一回继续解析inv_icm20948_set_slave_compass_id函数&#xff0c;解析了第3段代码&#xff0c;本回解析接下来的代码。为了便于理解和回顾&#xff0c;再次贴出该函数源码&#xff0c;在EMD-Core\so…

队列+宽搜专题篇

目录 N叉树的层序遍历 二叉树的锯齿形层序遍历 二叉树最大宽度 在每个树行中找最大值 N叉树的层序遍历 题目 思路 使用队列层序遍历来解决这道题&#xff0c;首先判断根节点是否为空&#xff0c;为空则返回空的二维数组&#xff1b;否则&#xff0c;就进行层序遍历&#x…

chapter 12 Bandgap References

chapter 12 Bandgap References 这一章我们介绍电压和电流基准. 首先学习和输入电压无关的偏置和起动问题. 然后介绍和温度有关的基准电压, 考虑offset对输出电压的影响. 最后学习恒定gm偏置和state-of-the-art电压基准. 12.1 General Considerations 一般基准电流有三种形式…

鸿蒙OpenHarmony【轻量系统芯片移植案例】标准系统方案之瑞芯微RK3566移植案例

标准系统方案之瑞芯微RK3566移植案例 本文章是基于瑞芯微RK3566芯片的khdvk_3566b开发板&#xff0c;进行标准系统相关功能的移植&#xff0c;主要包括产品配置添加&#xff0c;内核启动、升级&#xff0c;音频ADM化&#xff0c;Camera&#xff0c;TP&#xff0c;LCD&#xff…

数据集-目标检测系列-摩托车检测数据集 motorcycle >> DataBall

数据集-目标检测系列-摩托车检测数据集 motorcycle >> DataBall 数据集-目标检测系列-摩托车&#xff08;motorcycle&#xff09;检测数据集 数据量&#xff1a;1W 想要进一步了解&#xff0c;请联系 DataBall。 DataBall 助力快速掌握数据集的信息和使用方式&#xf…

AI 智能名片链动 2+1 模式商城小程序中的体验策略

摘要&#xff1a;本文探讨了在 AI 智能名片链动 21 模式商城小程序中&#xff0c;体验策略如何服务于用户体验&#xff0c;以及与产品策略的区别。重点分析了该小程序如何通过关注用户在使用过程中的流畅度、视觉体感等方面&#xff0c;实现“让用户用得爽”的目标&#xff0c;…

MWD天气图像多分类数据集,用于图像分类总共6个类别,多云,下雨,下雪,雾天,正常天气,共60000张图像数据

MWD天气图像多分类数据集&#xff0c;用于图像分类 总共6个类别&#xff0c;多云&#xff0c;下雨&#xff0c;下雪&#xff0c;雾天&#xff0c;正常天气&#xff0c;共60000张图像数据 MWD天气图像多分类数据集 (Multi-Weather Dataset, MWD) 数据集描述 MWD天气图像多分类…

哔哩哔哩自动批量删除抽奖动态解析篇(三)

前面两节已经介绍了如何获取抽奖动态列表和根据动态id_str索引值判断是否开奖了&#xff0c;接下来我们要完成的就是删除已经开奖了的动态 一、删除已经开奖的抽奖动态 老规矩&#xff0c;我们按F12健进入网页源代码&#xff0c;删除一条动态&#xff0c;观察网络接口 这次…

论文阅读 | 可证安全隐写(网络空间安全科学学报 2023)

可证安全隐写&#xff1a;理论、应用与展望 一、什么是可证安全隐写&#xff1f; 对于经验安全的隐写算法&#xff0c;即使其算法设计得相当周密&#xff0c;隐写分析者&#xff08;攻击者&#xff09;在观察了足够数量的载密&#xff08;含有隐写信息的数据&#xff09;和载体…

Redis数据结构之list列表

一.list列表 列表相当于数组或者顺序表 它里面的元素是有序的&#xff0c;也就是可以通过下标进行访问。这里的有序的含义是要根据上下文区分的&#xff0c;有的时候&#xff0c;有序指的是升序/降序&#xff0c;有的时候有序指的是顺序很关键&#xff0c;俩个元素交换后就不…

Spring6梳理10—— 依赖注入之注入数组类型属性

以上笔记来源&#xff1a; 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09;https://www.bilibili.com/video/BV1kR4y1b7Qc 目录 10 依赖注入之注入数组类型属性 10.1 创建Emp实体类&#xff0c;Dept实体类 10.2…