Dispatcher.BeginInvoke()方法使用不当导致UI界面卡死的原因分析

2023-05-16

前段时间,公司同事开发了一个小工具,在工具执行过程中,UI界面一直处于卡死状态。

通过阅读代码发现,主要是由于Dispatcher.BeginInvoke()方法使用不当导致的。

本文将通过一个WPF模拟程序来演示一下界面卡死的现象,并通过修改代码来解决界面卡死的问题。

希望通过对本文的学习,大家能对Dispatcher.BeginInvoke()方法有一个新的认识。

文章开篇直接给出界面卡死的示例代码。

示例WPF程序,用来计算1~n的和值,这里的n可以是1亿~25 亿之间的某个值,通过界面录入,结果显示在n输入框后面的文本框中,既然是WPF程序,代码包含xamlcs代码两部分,本文一并给出。

以下为cs代码:

using System;
using System.Windows;
using System.Threading;

namespace DispatcherExample
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            Int64 inputNumber;
            if (!Int64.TryParse(this.textBox1.Text, out inputNumber))
            {
                MessageBox.Show("请输入1亿-10亿皑间的整型数据!");
                return;
            }
            if (inputNumber > 2500000000 || inputNumber<100000000)
            {
                MessageBox.Show("请输入1亿-10亿间的整型数据!");
                return;
            }
            Thread newThread = new Thread(new ParameterizedThreadStart(GetResult));
            newThread.Start(inputNumber);
        }

        private void GetResult(object inputNumber)
        {
            this.Dispatcher.BeginInvoke((Action)delegate()
            {
                this.textBox2.Text = CalcSum((Int64)inputNumber).ToString();
            });
        }

        private double CalcSum(Int64 inputNumber)
        {
            double sum=0;
            for (int i = 0; i < inputNumber; i++)
            {
                sum +=i;
            }
            return sum;
        }
    }
}

以下为xaml代码:

<Window x:Class="DispatcherExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="求(和)你亿万次~~" Height="350" Width="525" ResizeMode="NoResize">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="252*" />
            <ColumnDefinition Width="251*" />
        </Grid.ColumnDefinitions>
        <Button Content="计算和值" Height="23" HorizontalAlignment="Left" Margin="213,168,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" Grid.ColumnSpan="2" />
        <Label Content="输入1亿-25亿间的数字:" Height="28" HorizontalAlignment="Left" Margin="36,93,0,0" Name="label1" VerticalAlignment="Top" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="158,96,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Grid.ColumnSpan="2" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="35,96,0,0" Name="textBox2" VerticalAlignment="Top" Width="177" Text="结果看这里..." Grid.Column="1" />
    </Grid>
</Window>

执行程序,界面如下:

输入2500000000,点击“计算和值”按钮,程序开始计算和值,界面卡死,无法再操作该程序(如移动位置或重新输入等)。

分析代码,发现问题应该出在下面的代码中,因为该部分代码中存在调用UI主线程的操作,此种操作不当往往会导致界面卡死的现象。

private void GetResult(object inputNumber)
{
     this.Dispatcher.BeginInvoke((Action)delegate()
     {
           this.textBox2.Text = CalcSum((Int64)inputNumber).ToString();
     });
}


那么,问题到底出在哪里呢?

要想弄清楚这点,还得了解一下Dispatcher.BeginInvoke()方法。

MSDN上对Dispatcher.BeginInvoke方法的解释如下

Dispatcher.BeginInvoke 方法 (Action)

在与 Dispatcher关联的线程上异步执行指定的委托。 

那么本实例中,与 Dispatcher关联的线程是什么呢?

要想弄清楚这点很简单。只要知道this.Dispatcher.BeginInvoke()中的this指的是什么就可以了。在Visual studio中将鼠标至于this上,发现this指的是当前的窗体类(如下图),即程序的主线程。

到这,我们应该知道问题出在哪里了。

原因是:在GetResult()方法中,将求和的操作交由主线程来完成,当计算未完成时,界面自然会被卡死。

通过与同事交谈了解到,他其实想要的是:新开一个线程来完成自己预想的运算(类似于示例程序中的求和运算),在结果出来后再调用主线程显示结果。

这样界面就不会出现卡死现象,但是上面的代码并没有达到预想结果。

原因前面已经交代了,因为这段代码将求和的计算仍然丢给了主线程,尽管新开了线程,但是新开线程并不进行求和运算,可以说是绕了一圈又回来了。

主线程开新线程,新线程又调用主线程。这有点像工作中的踢皮球,我给你一件事,你说不会,又踢回给我。

找到原因再修改就简单了,修改后的代码如下:

private void GetResult(object inputNumber)
{
     double result=CalcSum((Int64)inputNumber);
     this.Dispatcher.BeginInvoke((Action)delegate()
     {
           //this.textBox2.Text = CalcSum((Int64)inputNumber).ToString();
           this.textBox2.Text = result.ToString();          
     });
}

至于为什么要这样修改,我想:你懂的。

再次执行程序,输入2500000000,求和,界面不再存在卡死现象。

就扯到这里了,我要米西米西了,88

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

Dispatcher.BeginInvoke()方法使用不当导致UI界面卡死的原因分析 的相关文章

随机推荐

  • 常见分布式ID生成方案

    文章目录 一 为什么要用分布式ID1 什么是分布式ID2 那么分布式ID需要满足哪些条件 二 分布式ID有哪些生成方式1 基于UUID2 基于数据库自增ID3 基于数据库集群模式4 基于数据库的号段模式5 基于Redis模式6 基于雪花算法
  • 【思维升级】这几个学习方法,打破你的思维惯性,带你走向高效人生

    文章目录 A 如何做好计划1 如何做好长期计划和总结 xff1f 2 如何做好日计划 xff1a 待办日程清单 xff0c 工作从容不迫的武器 B 如何让学习更有效率1 极其功利的少读书2 极其功利的配置资源3 不要从第一页开始读书4 学习
  • 写着简单跑得又快的数据库语言 SPL

    文章目录 数据库语言的目标SQL为什么不行SPL为什么能行游离记录有序性离散性与集合化分组理解聚合理解有序支持的高性能 SPL资料 数据库语言的目标 要说清这个目标 xff0c 先要理解数据库是做什么的 数据库这个软件 xff0c 名字中有
  • 怎样提高报表呈现的性能

    文章目录 报表性能问题出在什么环节 xff1f 数据准备的问题和优化数据传输的问题和优化其他环节的问题和优化报表内计算和呈现 大报表总结润乾报表资料 报表的性能很重要 xff0c 是一个总被谈及的问题 xff0c 跑的慢的报表用户体验恶劣
  • 高考的意义是什么

    文章目录 创立 xff1a 率世界之先 xff0c 开启大学全国统一考试之路重启 xff1a 担公平之义 xff0c 全面打开人才进阶通道奋进 xff1a 选栋梁之才 xff0c 积蓄国家发展能量探索 xff1a 掀改革之潮 xff0c 坚
  • 自定义Maven Archetype模板工程

    文章目录 Maven Archetype介绍什么是Maven Archetype为什么要有模板工程创建模板工程的三种方式 常用的archetypemaven archetype quickstartmaven archetype webap
  • 全文搜索引擎 ElasticSearch 还是 Solr?

    文章目录 什么是全文搜索为什么要用全文搜索搜索引擎Lucene xff0c Solr xff0c ElasticSearch xff1f LuceneSolrElasticSearch Elasticsearch vs Solr的选择历史比
  • 正面管教-读书笔记

    正面管教 读书笔记 文章目录 正面管教 读书笔记正面管教 价值正面管教 作者简介正面管教 正文一 什么是正面管教不同的教养方式是什么在起作用 xff1f 和善与坚定并行 二 为什么要使用正面管教一 阿德勒的基本概念二 赢得 孩子三 赢得合作
  • 内存数据库如何发挥内存优势?

    文章目录 指针式复用外键预关联序号定位集群维表备胎式容错回顾与总结SPL资料 与以磁盘存储为主的普通数据库相比 xff0c 内存数据库的数据访问速度可以高出几个数量级 xff0c 能大幅提高运算性能 xff0c 更适合高并发 低延时的业务场
  • git rm工作区文件删除后撤销

    文章目录 1 现象描述2 解决3 add了多余的文件需要从add中删除4 commit中撤销文件4 1 查看commit的内容4 2 撤销commit里的一部分内容 1 现象描述 今天add文件的时候 xff0c 不小心把一个不需要提交的也
  • 如何建立自己的认知体系

    文章目录 什么是以及为什么要搭建知识体系如何建立知识体系 什么是元认知 什么是程序类知识 什么是概念类知识 什么是事实类知识 搭建知识过程中的注意事项1 无用之用是为大用2 让知识间建立关系 xff0c 切记一定不要孤立3 寻找一切现象 x
  • 【springboot系列】springboot整合guava实现本地缓存

    概述 Guava Cache 是 Google 开源的一套开发工具集合 xff0c Guava Cache 是其中的一个专门用于处理本地缓存的轻量级框架 xff0c 是全内存方式的本地缓存 xff0c 而且是线程安全的 和 Concurre
  • 解决Windows莫名其妙地从休眠状态唤醒的问题

    因为经常写代码开很多个工作区 xff0c 电脑经常就是合盖休眠 xff0c 这样打开就能用 xff0c 但是总是在我不经意的时候发现电脑风扇在狂转 xff0c 不知道是哪个应用又在qj我的电源管理计划 xff0c 今天午休发现电脑风扇狂转
  • Ubunt装机后的必要设置及必备软件

    1 为 Ubuntu Dock 启用 Minimize on Click Ubuntu Dock xff08 位于屏幕左侧的任务栏 xff09 可以轻松打开 xff0c 并且切换和管理应用程序与正在运行的应用程序 你可以点击 Dock 中的
  • No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android 解决方法

    在ndk版本升级之后 xff0c 项目编译出现了问题 xff0c 提示没有对应的编译工具链 xff0c 目前最多的做法是 xff0c 下载之前的ndk版本 xff0c 然后再拷贝缺少的部分 这种做法确实可以解决问题 xff0c 但是既然新版
  • 来自一个前端大神转产品经理后的聊天感悟

    给的学习建议 xff1a 1 推荐给我一本书 锋利的jQuery 2 学会使用思维导图工具 3 课余时间学习理财 4 研发过程中 xff0c 多多留心一些交互 xff0c 自己完善反复琢磨自己的思路 xff08 保证是最简的 xff09 5
  • 08丨案例:编写最简单的性能脚本

    通常我们会遇到要手写脚本的时候 xff0c 就要针对一些接口编写脚本 这时候 xff0c 我们需要知道接口规范和后台的数据是什么 而有些性能测试工程师写脚本时 xff0c 并不知道后端的逻辑 xff0c 只知道实现脚本 xff0c 事实上
  • KindEditor图片上传相关问题 (转)

    size 61 x large 从众多的Web编辑器中选择KindEditor xff0c 主要是看重它的小巧 一个JS文件 两个CSS文件和一个GIF图片就是它的全部 所以在页面上的加载速度很快 xff0c 而且功能也相对齐全 目前Kin
  • hexo+Ubuntu+github搭建个人博客(详细)

    菜鸟初步搭建须知 xff08 是我没错 xff0c 备忘 xff09 相应知识 会一些基本的Linux命令和vim的操作命令 可以在实验楼上入门学习网上已经浏览了 官方文档安装ubuntu和git xff08 因为最近在学习用ubuntu
  • Dispatcher.BeginInvoke()方法使用不当导致UI界面卡死的原因分析

    前段时间 xff0c 公司同事开发了一个小工具 xff0c 在工具执行过程中 xff0c UI 界面一直处于卡死状态 通过阅读代码发现 xff0c 主要是由于 Dispatcher BeginInvoke 方法使用不当导致的 本文将通过一个