(十)异步-使用异步(3)

一、GUI 程序中的异步操作

1、在 GUI 程序中使用异步操作

在 GUI程序中, 首先理解关于 UI 显示变化的概念。

  • 消息: UI 上的行为,如点击按钮、展示标签、移动窗体等。
  • 消息队列: 把要触发的所有消息,都按照相关的顺序放入到里面。
  • 消息泵: 消息泵主要从队列里提取出并处理该消息。
  • 处理程序代码: 与消息“绑定”的实现代码,如点击按钮消息,执行程序代码:btnDoStuff_Click。

因为在GUI 程序上对所有UI效果的变化都必须在主 GUI 线程中完成的,所以 UI主线程通过消息泵来对UI消息进行处理。

消息泵从队列中取出一条消息,并调用它的处理程序代码。当处理程序代码完成时,消息泵获取下一条消息并循环这个过程。

请添加图片描述

但是如果某个消息的处理程序代码耗时过长,消息队列中的消息会产生积压(消息没有及时进行移除并处理),程序将会一时失去响应,在等待当前处理程序代码处理完成之前。没法处理其他任何消息。

以下是WPF 应用程序中处理程序代码中使用了 Thread.Sleep 的示例:

xaml代码:

    <StackPanel>
        <Label Name="lbStatus" Margin="10,5,10,0">Not Doing Anything</Label>
        <Button Name="btnDoStuff" Content="Do Stuff" HorizontalAlignment="Left" Margin="10,5" Padding="5,2" Click="btnDoStuff_Click"></Button>
    </StackPanel>

后台cs代码:

private void btnDoStuff_Click(object sender, RoutedEventArgs e)
        {
            btnDoStuff.IsEnabled = false;
            lbStatus.Content = "Doing Stuff";
            Thread.Sleep(4000);
            lbStatus.Content = "Not Doing Anything";
            btnDoStuff.IsEnabled = true;
        }

运行结果:

lbStatus 标签看起来没有改变,是因为还没有及时把刷新标签内容的消息进行处理,就直接暂停线程了。4秒后线程重新工作,这时其实是有把标签改变为 Doing Stuff 的,但是处理的时间太快,就立马执行下一条刷新的消息,所以肉眼上看不出内容改变的效果。如果把 lbStatus.Content = “Not Doing Anything”; 这条语句注释掉,就会发现4秒后标签就显示为 Doing Stuff 。

请添加图片描述

如果把 Thread.Sleep 改为Task.Delay,点击按钮后就不会卡顿。这是因为 Thread.Sleep 会阻塞线程,而 Task.Delay 不会阻塞线程(暂时不处理对应消息的处理程序代码),线程还可以继续处理其他工作。(其他触发的消息)

2、Task.Yield

Task.Yield 方法创建一个立即返回的 awaitable。当异步方法在处理程序代码里被调用时,异步方法里的 每次执行一个 await Task.Yield,就会把当前消息从消息队列中移除,再回到队列末尾,移交控制权给其他任务由处理器继续处理。
跟 Task.Delay 不同的一点就是:

  • 调用一次 Task.Yield 就会把消息重新回到队列末尾中,重新在等到它时才会继续处理后续部分代码。
  • 调用Task.Delay 设置一定时间内,先处理其他消息,等时间到了,才会继续处理它的后续部分代码。

使用异步 Lambda 表达式

XAML:

<StackPanel>
       <TextBlock Name="workStartedTextBlock" Margin="10,10"/>
        <Button Name="StartWorkButton" Width="100" Margin="4" Content="Start Work"/>
</StackPanel>

后台 CS 代码:

 public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //异步 Lambda 表达式:async (sender, e) =>
            StartWorkButton.Click += async (sender, e) =>
            {
                SetGuiValues(false, "Work Started");
                await DoSomeWork();
                SetGuiValues(true, "Work Finished");
            };
        }

        private void SetGuiValues(bool buttonEnabled,string status)
        {
            StartWorkButton.IsEnabled = buttonEnabled;
            workStartedTextBlock.Text = status;
        }

        private Task DoSomeWork()
        {
            return Task.Delay(2500);
        }
    }

二、BackgroundWorker 类

1、BackgroundWorker 类

该类创建一个新线程,是属于后台线程,在后台持续运行以完成某项工作,并不时地与主线程通信。

请添加图片描述

类的主要成员:
属性:

  • WorkerReportsProgress 和 WorkerSupportsCancellation: 这两个属性用于设置后台任务是否可以把它的进度汇报给主线程以及是否支持从主线程取消。
  • IsBusy: 检查后台任务是否正在运行。

事件:
用于发送不同的程序事件和状态。

  • DoWork:后台线程开始的时候触发。
  • ProgressChanged:后台任务汇报进度的时候触发。
  • RunWorkerCompleted:后台工作线程退出的时候触发。

方法:
用于开始行为或改变状态。

  • RunWorkerAsync: RunWorkerAsync 方法获取后台线程并且执行 DoWork 事件处理程序。

  • CancelAsync: 调用 CancelAsync 方法把 CancellationPending 属性设置为 true。DoWork 事件处理程序需要检查这个属性来决定是否应该停止处理。

  • DoWork: DoWork 事件处理程序(在后台线程)在希望向主线程汇报进度的时候,调用 ReportProgress 方法。

DoWork 是必需的,该事件跟事件处理程序关联,包含有在后台线程执行的代码。
ProgressChanged 和 RunWorkerCompleted 是可选的,取决于程序的需要。

后台线程处理程序的原理:

  • DoWork 事件包含后台线程上执行的代码。

    • 主线程调用 BackgroundWorker 对象的 RunWorkerAsync 方法的时候会触发 DoWork 事件。
  • 后台线程通过调用 ReportProgress 方法与主线程通信。届时将触发 ProgressChanged 事件,主线程可以附加到 ProgressChanged 事件上的处理程序处理事件。

  • 附加到 RunWorkerCompleted 事件的处理程序应该包含在后台线程完成 DoWork 事件处理程序的执行之后需要执行的代码。(即结束该后台线程工作的最后一个事情)

请添加图片描述

主要事件的声明如下:

void DoWorkEventHandler(object sender, DoWorkEventArgs e)
void ProgressChangedEventHandler(object sender, ProgressChangedEventArgs e)
void RunWorkerCompletedEventHandler(object sender, RunWorkerCompletedEventArgs e)

请添加图片描述

2、配置 BackgroundWorker 类对象:

  • 若需要工作线程向主线程汇报进度,把 WorkerReportsProgress 属性设置为 true。(启用的话,会调用 ReportProgress 方法,从而触发 ProgressChanged 事件。)

  • 若从主线程取消工作线程,就把 WorkerSuppoortsCancellation 属性设置为 true。(启用的话,DoWork 事件处理程序代码会定期检查 CancellationPending 属性是否取消了。若是,则退出。)

类对象配置好后,调用 RunWorkerAsync 方法来启动它。

在 WPF 程序中使用 BackgroundWorker 类的示例:

XAML 代码:

   <StackPanel>
     <ProgressBar Name="progressBar" Height="20" Width="200" Margin="100"/>
        <Button Name="btnProcess" Width="100" Click="btnProcess_Click"
                Margin="5">Process</Button>
        <Button Name="btnCancel" Width="100" Click="btnCancel_Click"
                Margin="5">Cancel</Button>
    </StackPanel>

后台 cs 代码:

    public partial class MainWindow : Window
    {
        BackgroundWorker bgWorker = new BackgroundWorker();
        public MainWindow()
        {
            InitializeComponent();

            //设置 BackgroundWorker 属性
            bgWorker.WorkerReportsProgress = true;
            bgWorker.WorkerSupportsCancellation = true;

            //连接 BackgroundWorker 对象的处理程序
            bgWorker.DoWork += DoWork_Handler;
            bgWorker.ProgressChanged += ProgressChanged_Handler;
            bgWorker.RunWorkerCompleted += RunWorkerCompleted_Handler;
        }

        private void btnProcess_Click(object sender, RoutedEventArgs e)
        {
            if (!bgWorker.IsBusy)
                bgWorker.RunWorkerAsync();
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            bgWorker.CancelAsync();
        }

        private void ProgressChanged_Handler(object sender,ProgressChangedEventArgs args)
        {
            progressBar.Value = args.ProgressPercentage;
        }

        private void DoWork_Handler(object sender, DoWorkEventArgs args)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            for(int i = 1; i <= 10;i++)
            {
                if(worker.CancellationPending)
                {
                    args.Cancel = true;
                    break;
                }
                else
                {
                    worker.ReportProgress(i * 10);
                    Thread.Sleep(500);//这里当前线程为后台线程
                }
            }
        }

        private void RunWorkerCompleted_Handler(object sender, RunWorkerCompletedEventArgs args)
        {
            progressBar.Value = 0;
            if (args.Cancelled)
                MessageBox.Show("Process was cancelled.", "Process Cancelled");
            else
                MessageBox.Show("Process completed normally.", "Process Completed");
        }

    }

三、并行循环

任务并行库是 BCL 中的一个类库。
如果迭代之间彼此独立,并且程序运行在多处理器机器上。

使用条件: 迭代之间彼此独立。每=次迭代运行的过程和结果都互相不影响彼此,如计算多个不同范围的面积。若每次迭代都叠加数值最终得出一个总数,则不适合使用并行循环。

1、Parallel.For:

public static ParallelLoopResult.For(int fromInclusive, int toExclusive, Action body);
  • fromInclusive: 迭代系列的第一个整数。
  • toExclusive: 比迭代系列最后一个索引号大1的整数。和使用表达式 index<ToExclusive 计算一样。
  • body: 接受单个输入参数的委托,body 的代码在每一次迭代中执行一次。
class Program
{
static void Main()
{
Parallel.For(0,15,i =>
			Console.WriteLine($"The square of{ i } is { i * i}"));
}
}

输出结果:

The square of 0 is 0
The square of 1 is 1
The square of 2 is 4
The square of 4 is 16
The square of 5 is 25
The square of 6 is 36
The square of 7 is 49
The square of 8 is 64
The square of 9 is 81
The square of 10 is 100
The square of 11 is 121
The square of 12 is 144
The square of 13 is 169
The square of 14 is 196
The square of 3 is 9

整数数组的示例:

class Program
{
static void Main()
{
const int maxValues = 50;
int[] squares = new int[maxValues];

Parallel.For(0,maxValues,i => squares[i] = i *i);
}
}

2、Parallel.ForEach

Parallel.ForEach 方法有相当多的重载,其中最简单的如下:

static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source,Action<TSource> body)

示例:

    class Program
    {
        static void Main(string[] args)
        {
            string[] squares = new string[]
                { "We","hold","these","truths","to","be","self-evident",
                  "that","all","men","are","created","equal"};

            Parallel.ForEach(squares, s => Console.WriteLine(string.Format($"\"{ s }\" has { s.Length } letters")));
            Console.ReadKey();
        }
    }

输出结果:

“We” has 2 letters
“truths” has 6 letters
“to” has 2 letters
“be” has 2 letters
“self-evident” has 12 letters
“that” has 4 letters
“all” has 3 letters
“men” has 3 letters
“are” has 3 letters
“created” has 7 letters
“equal” has 5 letters
“hold” has 4 letters
“these” has 5 letters

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CSDN 周赛 59 期

CSDN 周赛 59 期 前言判断题单选题题目1题目2填空题编程题1、题目名称:坏掉的打字机2、题目名称:布尔零点计数小结前言 由于最近,csdn 每日一练新增了两个题目,按照惯例,那么新增的题目,会就近出现在最近的 CSDN 周赛中,嗯,经常参加周赛,并关注每日一练社区的小伙伴应…

IO流(C++)

IO流C C语言的输入与输出流是什么CIO流C标准IO流C文件IO流二进制读写文本读写 stringstream的简单介绍 C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键 盘)读取数据&#xff0c;并将值存放在变量中。printf():…

阿里云国际站代理商:如何优化阿里云服务器的性能和响应速度?有哪些调优策略和建议?

随着互联网的发展&#xff0c;阿里云服务器已经成为很多企业和个人的首选解决方案。然而&#xff0c;面对不断增长的需求和复杂的网络环境&#xff0c;如何优化阿里云服务器的性能和响应速度&#xff0c;提高用户体验&#xff0c;是很多用户关心的问题。本文将从以下几个方面&a…

MsSqlServer配置管理器TCP/IP属性

TCP/IP 属性&#xff08;“IP 地址”选项卡&#xff09; 使用 “TCP/IP 属性&#xff08;‘IP 地址’选项卡&#xff09;” 对话框&#xff0c;可以配置特定 IP 地址的 TCP/IP 协议选项。 只有选中 “IP All” &#xff0c;才能一次配置所有地址的 “TCP 动态端口” 和 “TCP…

Debian openssh-server 的安装

在之前安装系统的时候有一个安装 SSH 服务的&#xff0c;结果没点上&#xff0c;导致系统完成后&#xff0c;ssh无法连接上啊&#xff0c;于是要安装sshd 服务。使用命令&#xff1a;apt-get install openssh-server 结果就出现问题了&#xff1a; 网上搜索说是要更新源&#x…

catkin cmake官方教程解读以及资料补充

这里写目录标题 报错cmakei下载cmake 官方教程教程1step1最低版本 报错报错2 vscode 路径没有配置好setting.json通过该方式打开的似乎是一个全局的文件&#xff0c;可以为本工作文件夹下设置一个本地的吗 报错3配置cmake工具链准确的流程报错4 cpp中main函数返回值问题结果 官…

Verilog基础:标识符的向上向下层次名引用

相关文章 Verilog基础&#xff1a;表达式位宽的确定&#xff08;位宽拓展&#xff09; Verilog基础&#xff1a;表达式符号的确定 Verilog基础&#xff1a;数据类型 Verilog基础&#xff1a;位宽拓展和有符号数运算的联系 Verilog基础&#xff1a;case、casex、ca…

如何在Microsoft Excel中使用TRUNC函数

Excel 中有多种删除小数点和缩短数值的方法。在本文中,我们将解释如何使用 TRUNC 函数,以及它与其他技术的不同之处。 TRUNC函数 什么是 TRUNC 功能如何使用 TRUNC 函数从日期时间戳中删除时间什么是 TRUNC 功能 TRUNC 函数将数字截断为指定的小数位数。使 TRUNC 不同于其他…

概率论与数理统计教程第五章节笔记

参考书籍&#xff1a;概率论与数理统计教程第三版 茆诗松 程依明 濮晓龙 编著 文章声明&#xff1a;如有错误还望批评指正 文章目录 ξ 5.1 \xi5.1 ξ5.1总体与样本 ξ 5.2 \xi5.2 ξ5.2样本数据的整理与显示Python绘制直方图Python绘制茎叶图 ξ 5.3 \xi5.3 ξ5.3统计量及其分…

基於Hadoop HA 在kerberos中配置datax

概要 提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 概要 前言一、基於HADOOP HA 搭建datax二、基於HADOOP HA 配置好的datax去配置kerberos1.在datax的配置文件中進行配置2.在shell腳本中加入認證語句 总结 前言…

layui框架学习(27:弹出层模块_其它用法)

除了前几篇文章介绍的弹出框类型外&#xff0c;layui的layer弹出层模块还支持相册框和tab框&#xff0c;所谓相册框即点击图片或按钮后会出现一个类似相册的页面单独浏览、切换图片&#xff0c;而tab框是指弹出框的显示形式类似于Winform中的TabControl控件&#xff0c;能以选项…

【Spring Security】的RememberMe功能流程与源码详解

文章目录 前言原理 基础版搭建初始化sql依赖引入配置类验证 源码分析 进阶版集成源码分析疑问1疑问2 鉴权 升级版集成初始化sql配置类验证 源码分析鉴权流程 扩展版 前言 之前我已经写过好几篇权限认证相关的文章了&#xff0c;有想复习的同学可以查看【身份权限认证合集】。今…

MIT 6.S081 Lab Four

MIT 6.S081 Lab Four 引言trapsRISC-V assembly (easy)代码解析 Backtrace(moderate)代码解析 Alarm(Hard)test0: invoke handler(调用处理程序)test1/test2(): resume interrupted code(恢复被中断的代码)代码解析issue解答 可选的挑战练习 引言 本文为 MIT 6.S081 2020 操作…

JDBC BasicDAO详解(通俗易懂)

目录 一、前言 二、BasicDAO的引入 1.为什么需要BasicDAO&#xff1f; 2.BasicDAO示意图 : 三、BasicDAO的分析 1.基本说明 : 2.简单设计 : 四、BasicDAO的实现 0.准备工作 : 1.工具类 : 2.JavaBean类 : 3.BasicDAO类 / StusDAO类 : 4.测试类 : 一、前言 第七节内容…

Jenkins+Docker 实现一键自动化部署项目!步骤齐全,少走坑路

大家好&#xff0c;我是互联网架构师&#xff01; 本文章实现最简单全面的Jenkinsdockerspringboot 一键自动部署项目&#xff0c;步骤齐全&#xff0c;少走坑路。 环境&#xff1a;centos7git(gitee) 简述实现步骤&#xff1a;在docker安装jenkins&#xff0c;配置jenkins基…

数据结构:二叉树经典例题(单选题)-->你真的掌握二叉树了吗?(第一弹)

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关二叉树的经典例题&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通 数…

Sentinel的限流和Gateway的限流差别?

Sentinel的限流与Gateway的限流有什么差别&#xff1f; 问题说明&#xff1a;考察对限流算法的掌握情况 限流算法常见的有三种实现&#xff1a;滑动时间窗口&#xff0c;令牌桶算法&#xff0c;漏桶算法。gateway则采用基于Redis实现的令牌桶算法。但是我们不会去用&#xff…

ubuntu常用命令

设置root密码 安装好ubuntu后谁也不知道root密码是多少&#xff0c;可以借助于passwd命令来设置root密码。 sudo passwd root 同理修改其他用户只需替换上方用户名即可 换源 备份原始文件 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak 修改源文件 sudo vim /et…

数据链路层(MAC)、网络层(IP)、传输层(TCP/UDP)抓包分析

目录 OSI七层模型数据包逐层封装头部抓包分析数据包概况数据链路层抓包网络层抓包&#xff08;IP协议抓包&#xff09;UDP抓包数据负载抓包 Linux cooked-mode capture OSI七层模型 OSI模型&#xff08;OSI model&#xff09;&#xff0c;开放式系统互联通信参考模型&#xff…

台电x80HD 安装linux系统,可调电压电源供电,外网访问、3D打印klipper固件

一、系统安装 参照https://blog.csdn.net/gangtieren/article/details/102975027安装 安装过程遇到的问题&#xff1a; 1、试了 linux mint 21 、ubuntu20.04 、ubuntu22.04 都没有直接安装成功&#xff0c;u盘选择安装进入系统后一直黑屏&#xff0c;只有ubuntu18.04 选择后稍…