【C#】ProgressBar进度条异步编程思想

news2024/11/18 1:44:44

1.控件介绍

进度条通常用于显示代码的执行进程进度,在一些复杂功能交互体验时告知用户进程还在继续。
在这里插入图片描述
在属性栏中,有三个值常用:
Value表示当前值,Minimum表示进度条范围下限,Maximum表示进度条范围上限。

2.简单实例

在一个界面下,点击按钮,进度条加载,用label显示运行耗时。
在这里插入图片描述

在Form1.cs中,添加点击Button1按钮功能:

private async void button1_Click(object sender, EventArgs e)
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    button1.Enabled = false;//防止重复点击
    progressBar1.Value = 0;
    int progressStep = 10;
    for (int i = 0; i <= progressBar1.Maximum; i = i + progressStep)
    {
        await Task.Delay(100);
        progressBar1.Value = i;
    }
    button1.Enabled = true;
    stopwatch.Stop();
    label1.Text = $"运行耗时:{stopwatch.ElapsedMilliseconds}ms";
    MessageBox.Show("ok");
}

3.异步编程思想:

异步编程是一种编程范式,它允许程序在执行耗时操作时,不阻塞主线程或调用线程,从而提高程序的响应性和性能。简单来说,异步编程使得程序在等待某些操作(如网络请求、文件I/O、数据库查询等)的完成时,可以继续处理其他任务。

3.1 为什么需要异步编程?

在同步编程模式下,当程序执行一个耗时操作(例如读取文件或从网络获取数据)时,整个程序会暂停,主线程被堵塞,直到这个操作完成。这会导致程序变得不可响应,特别是在需要处理用户交互的应用程序中。
异步编程通过允许程序处理其他任务而不会被耗时操作阻塞,解决了这一问题。例如,在用户接口应用程序中,异步编程可以防止界面“卡死”,从而提升用户体验。

3.2 异步编程流程:

功能代码如下:模拟一个等待执行

private async Task LoadDataAsync()
{
    await Task.Delay(5000);  // 异步等待 5 秒
    Console.WriteLine("数据加载完成");  // 5 秒后执行
}

(1)当线程执行到await关键字标识的位置后,系统将方法挂起,返回控制权给调用者。
(2)任务调度器记录 Task.Delay(5000),在 5 秒后标记任务为完成。
(3)5秒后,任务调度器标记 Task.Delay(5000) 完成。调度器触发回调,通知方法恢复执行。打印"数据加载完成"。
(4)在挂起期间,用户可以自行操作,不会造成UI阻塞。

3.3 await是什么?

await是C#中的关键字,用于异步编程等待异步操作的完成,不会阻塞当前进程。通常与async关键字一起用。

3.4 async是什么?

async也是C#中的关键字,用于修饰方法、匿名函数或者lambda表达式。通常和await一起用,指示他们包含异步操作

3.5 Task是什么?

异步方法通常返回 Task 或 Task 对象,表示一个异步操作的进行。
Task 类还可以用于表示和管理异步操作。

以上三个关键字总结:async 修饰的方法通常返回 Task 或 Task < T >,而 await 用于等待任务的完成。

3.6 应用例子:

在图形用户界面(GUI)应用程序中,阻塞主线程会导致用户界面变得不可响应。例如,如果用户点击一个按钮触发一个耗时操作,整个界面会在操作完成之前冻结,无法响应用户的其他操作。
单纯摆出异步编程的例子无法体会精髓,先用一个同步编程的来对比:

同步编程:

点击button1按钮,开始执行功能代码,代码功能放到了另一个方法中去,该方法的功能就是单纯进行线程休眠,模拟耗时操作。

private void button1_Click(object sender, EventArgs e)
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    button1.Enabled = false;//防止重复点击
    Form1Bar.Value = 0;
    int progressStep = 10;
    for (int i = 0; i <= Form1Bar.Maximum; i = i + progressStep)
    {
        // 同步方法,阻塞UI线程
        LoadData();
        Form1Bar.Value = i;
    }
    button1.Enabled = true;
    stopwatch.Stop();
    label1.Text = $"运行耗时:{stopwatch.ElapsedMilliseconds}ms";
    MessageBox.Show("ok");
}

模拟耗时方法:

private void LoadData()
{
    System.Threading.Thread.Sleep(1000);// 会强制阻塞线程
}

测试卡死按钮:

private void button2_Click(object sender, EventArgs e)
{
    textBox1.Text = "异步编程,UI未卡死";
}
private void button3_Click(object sender, EventArgs e)
{
    textBox1.Text = string.Empty;
}

在这里插入图片描述

运行过程中无法点击测试按钮,UI进程阻塞,GUI卡死。

异步编程(主窗口进度条):

异步编程会挂起当前await的耗时方法,不会阻塞当前线程,用户可以操作其他。

private async void button1_Click(object sender, EventArgs e)
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    button1.Enabled = false;//防止重复点击
    Form1Bar.Value = 0;
    int progressStep = 10;
    for (int i = 0; i <= Form1Bar.Maximum; i = i + progressStep)
    {
        // 同步方法,阻塞UI线程
        await LoadData();
        Form1Bar.Value = i;
    }
    button1.Enabled = true;
    stopwatch.Stop();
    label1.Text = $"运行耗时:{stopwatch.ElapsedMilliseconds}ms";
    MessageBox.Show("ok");
}
private async Task LoadData()
{
    //System.Threading.Thread.Sleep(5000);// 会强制阻塞线程
    await Task.Delay(500);
}

在这里插入图片描述

异步编程(弹出窗口进度条):

Form1作为主界面,只放Button1和label1两个控件,点击开始后,弹出Form2进度条加载。
不仅实现弹出窗口进度条,还通过异步编程实现。
关键代码:

private async void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;//防止重复点击

    Form2 form2 = new Form2();
    form2.Show();
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    for(int i = 0; i<= form2.Form2Bar.Maximum; i+= 10)
    {
        await LoadData();
        form2.Form2Bar.Value = i;
    }

    button1.Enabled = true;
    stopwatch.Stop();
    label1.Text = $"运行耗时:{stopwatch.ElapsedMilliseconds}ms";
    MessageBox.Show("ok");
    form2.Close();
}
private async Task LoadData()
{
    await Task.Delay(500);
}

在这里插入图片描述

4.更进一步

仔细阅读上述代码可以发现,每次进度条加载是通过i来控制的,i每次随着休眠结束会自增10,这样确实可以均匀控制进度条增长。
但是现在有这样一个问题,在实际项目中,我们随着处理的数据量不同,并不知道每次运行的固定时间,换言之,可能并不是每次均匀增长一个固定值。

在之前先介绍一下一种特殊的函数-----回调函数

4.1 回调函数

回调函数,是指函数通过参数传给另一个函数,在满足特定的条件下由后者调用。
在异步编程思想中,当某个操作完成后,回调函数会被执行,处理或相应发生的事件。
下面是一个简单的代码展示:

// 定义一个回调函数
void CallbackFunction(string message)
{
    Console.WriteLine(message);
}

// 定义一个异步函数,接受回调函数作为参数
void doSomethingAsync(Action<string> callback)
{
    // 模拟异步操作
    Task.Run(() =>
    {
        // 模拟一些工作
        Task.Delay(1000).Wait();
        
        // 调用回调函数
        callback("Operation completed!");
    });
}

// 使用异步函数并传递回调函数
doSomethingAsync(CallbackFunction);

CallbackFunction(string message)是一个回调函数,当有字符串类型的参数传入时,会进行打印操作。
doSomethingAsync(Action callback)是一个异步函数(内部含有Task.Run),内部模拟了一个耗时异步操作,在结束后调用回调函数。

4.2 控制进度条

在不同任务耗时不一样的前提下,控制进度条的增长可以通过下面两种方法:
(1)将任务分解成为多个子任务,每个任务结束后手动增加,更新进度条。(看起来一卡一卡的)
(2)通过IProgress < T > 接口实现任务的进度报告,实时更新进度条。(进度均匀,更优雅)

两种方法其实很类似,都是需要去做一个标记,然后更新。

如果实在无法分割子任务,可使用进度条Marquee样式,实现类似跑马灯的效果,只告诉用户程序在运行,不知道结束的时间。

关键代码实例,只保留核心部分:
progress是一个报告器,接受参数,触发内部的Lambda回调函数,更新进度条。

private async void Button_Click(object sender, EventArgs e)
{
    // 创建一个进度报告器,更新进度条
    var progress = new Progress<int>(percent =>
    {
        progressBar.Value = percent; // 更新进度条
    });
    // 启动长时间运行的任务
    await ExecuteLongRunningTask(progress);
    // ...
}
private async Task ExecuteLongRunningTask(IProgress<int> progress)
{
    int totalSteps = 100; // 任务的总步数(假设任务可以分为100步)
    for (int i = 0; i < totalSteps; i++)
    {
        // 模拟长时间任务
        await Task.Delay(100);  // 每步等待100毫秒
        // 报告进度
        progress.Report((i + 1) * 100 / totalSteps);
    }
}

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

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

相关文章

探索人工智能在电子商务平台与游戏发行商竞争中几种应用方式

过去 12 年来&#xff0c;电脑和视频游戏的发行策略发生了巨大变化。数字游戏的销量首次超过实体游戏的销量 在20132020 年的封锁进一步加速了这一趋势。例如&#xff0c;在意大利&#xff0c;封锁的第一周导致数字游戏下载量 暴涨174.9%. 展望未来&#xff0c;市场有望继续增…

配置基于不同IP地址的虚拟主机

定义配置文件vhost.conf <directory /www> allowoverride none require all granted </directory> <virtualhost 192.168.209.136:80> documentroot /www servername 192.168.209.136 </virtualhost><virtualhost 192.168.209.138:80> document…

电阻负载柜或无功负载组?

选择正确电源测试解决方案的快速指南 如果您在市场上寻找负载组&#xff0c;您无疑会遇到电阻式和反应式这两个术语。为了使负载组规范尽可能简单&#xff0c;您需要了解不同类型的负载测试解决方案之间的区别&#xff0c;以及哪种解决方案最适合您的应用。 什么是电阻负载组&…

AntDesign上传组件upload二次封装+全局上传hook使用

文章目录 前言a-upload组件二次封装1. 功能分析2. 代码详细注释3. 使用到的全局上传hook代码4. 使用方式5. 效果展示 总结 前言 在项目中&#xff0c;ant-design是我们常用的UI库之一&#xff0c;今天就来二次封装常用的组件a-upload批量上传组件,让它用起来更方便。 a-uploa…

Gartner发布软件供应链安全指南:软件供应链攻击造成的损失将从 2023 年的460亿美元上升到2031年的1380亿美元

软件供应链安全是一个关键的风险和合规性问题&#xff0c;但大多数组织都以分散的方式处理它。缺乏一个包罗万象的框架会遗留安全漏洞。通过实施三支柱框架&#xff0c;安全和风险管理领导者可以确保广泛的保护。 主要发现 对软件供应链的攻击给组织带来重大的安全、监管和运营…

Twitter群发消息API接口的功能?如何配置?

Twitter群发消息API接口怎么申请&#xff1f;如何使用API接口&#xff1f; 为了方便企业和开发者有效地与用户互动&#xff0c;Twitter提供了各种API接口&#xff0c;其中Twitter群发消息API接口尤为重要。AokSend将详细介绍Twitter群发消息API接口的功能及其应用场景。 Twit…

船舶雷达与导航中M7/8防水插座应用优势

船舶雷达与导航系统是船舶安全航行的重要组成部分&#xff0c;而7/8防水插座在这些系统中起着至关重要的作用。其中防水MIN-change 7/8"航空法兰插座成型预铸电缆式、组装式、面板式法兰座、T-型三通可选 7/8防水插座的电气性能 7/8防水插座因其优良的电气性能而被广泛应…

【matlab 路径规划】基于改进遗传粒子群算法的药店配送路径优化

一 背景介绍 本文分享的是一个基于订单合并的订单分配和路径规划联合优化&#xff0c;主要背景是骑手根据客户需求&#xff0c;从药店取药之后进行配送&#xff0c;配送的过程中考虑路径的长度、客户的服务时间窗、车辆的固定成本等要素&#xff0c;经过建模和优化得到最优的配…

收银系统源码-营销活动-幸运抽奖

1. 功能描述 营运抽奖&#xff1a;智慧新零售收银系统&#xff0c;线上商城营销插件&#xff0c;商户/门店在小程序商城上设置抽奖活动&#xff0c;中奖人员可内定&#xff1b; 2.适用场景 新店开业、门店周年庆、节假日等特定时间促销&#xff1b;会员拉新&#xff0c;需会…

【漏洞复现】万户协同办公平台——反序列化

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 万户协同办公平台ezEIP是一个综合信息基础应用平台&#xff0c;…

14-11 2024 年的 13 个 AI 趋势

2024 年的 13 个 AI 趋势 人工智能对环境的影响和平人工智能人工智能支持的问题解决和决策针对人工智能公司的诉讼2024 年美国总统大选与人工智能威胁人工智能、网络犯罪和社会工程威胁人工智能治疗孤独与对人工智能的情感依赖人工智能影响者中国争夺人工智能霸主地位人工智能…

上海时尚新品发布会,可以邀请哪些媒体

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 在上海举办时尚新品发布会时&#xff0c;可以邀请的媒体类型多样&#xff0c;以下是一些建议的媒体类型及其特点&#xff1a; 一、平面媒体 报纸&#xff1a; 《文汇报》&#xff1a;上…

【带你全面了解 RAG,深入探讨其核心范式、关键技术及未来趋势】

文末有福利&#xff01; 大型语言模型&#xff08;LLMs&#xff09;已经成为我们生活和工作的一部分&#xff0c;它们以惊人的多功能性和智能化改变了我们与信息的互动方式。 然而&#xff0c;尽管它们的能力令人印象深刻&#xff0c;但它们并非无懈可击。这些模型可能会产生…

python-图像旋转(赛氪OJ)

[题目描述] 输入一个 n 行 m 列的黑白图像&#xff0c;将它顺时针旋转 9090 度后输出。输入&#xff1a; 第一行包含两个整数 n 和 m&#xff0c;表示图像包含像素点的行数和列数。1≤n≤100&#xff0c;1≤m≤100。 接下来 n 行&#xff0c;每行 m 个整数&#xff0c;表示图像…

【FreeRTOS】同步与互斥通信-有缺陷的互斥案例

目录 同步与互斥通信同步与互斥的概念同步与互斥并不简单缺陷分析汇编指令优化过程 - 关闭中断时间轴分析 思考时刻 参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》 同步与互斥通信 同步与互斥的概念 一句话理解同步与互斥&#xff1a;我等你用完厕所&#xff0c;我再…

【Python学习笔记】菜鸟教程Scrapy案例 + B站amazon案例视频

背景前摇&#xff08;省流可以跳过这部分&#xff09; 实习的时候厚脸皮请教了一位办公室负责做爬虫这块的老师&#xff0c;给我推荐了Scrapy框架。 我之前学过一些爬虫基础&#xff0c;但是用的是比较常见的BeautifulSoup和Request&#xff0c;于是得到Scrapy这个关键词后&am…

【2023ICPC网络赛I 】E. Magical Pair

当时在做洛谷U389682 最大公约数合并的时候我就想到把每个质因子分解出来然后跑高维前缀和&#xff0c;但是那一道题不是用这个方法&#xff0c;所有我也一直在思考这种做法是不是真的有用。因为昨天通过2024上海大学生程序设计竞赛I-六元组计数这道题我了解到了不少关于原根的…

印章谁在管、谁用了、用在哪?契约锁让您打开手机一看便知

“印章都交给谁在管”、“哪些人能用”、“都有哪些业务在用”…这些既是管理者最关心的印章问题也是影响印章安全的关键要素。但是公司旗下分子公司那么多&#xff0c;各类公章、法人章、财务章、合同章一大堆&#xff0c;想“问”明白很难。 契约锁电子签及印控平台推出“印章…

【FreeRTOS】同步互斥与通信 有缺陷的同步示例

目录 1 同步互斥与通信1.1 同步互斥与通信概述1.2 同步与互斥的概念1.3 同步的例子&#xff1a;有缺陷1.4 freertos.c源码3. 互斥的例子&#xff1a;有缺陷4. 通信的例子&#xff1a;有缺陷5. FreeRTOS的解决方案 1 同步互斥与通信 1.1 同步互斥与通信概述 参考《FreeRTOS入门…