C#多线程加载控件界面卡死的解决

2023-05-16

先听一个故事:

有一个老板忙不过来,于是招一个员工去负责某些事务。这样老板就可以腾出时间处理其它事。

后来发现员工干不下去,原因是干活需要花费,没有老板的认可,财务不给批钱。这是原则。

于是老板设计了财务审批制度,要求员工涉及到报销的时候,要回公司处理审批。

结果员工干脆守着老板办公,随时征求老板的意见。于是老板还是很忙,没办法干别的事。

所以需要改善员工的工作方法,把有关财务报销的事总结并提炼出来,必要的时候才回来报销,没必要的时候不用总守着老板。于是老板终于可以时间自由了。

这个故事其实就是多线程的原理。老板就相当于主程序的UI线程,负责响应一些外来事务,员工就是线程,干一些具体耗时的工作。

我已经不是个称职的程序员了,所以高手可以略过,希望本文能给刚学习多线程编程的朋友一些启发。

曾经羡慕那些成熟大师的作品,它在执行一个耗时很长的任务时,而它的界面又是可以灵活操作的。我最初的想法,希望线程各干各的活,耗时的过程用一个独立的线程实现,而后线程之间可以完全对象化互相访问,比如跨线程访问控件。原以为这样就可以用独立的线程更新界面了,而且主线程(UI线程)不卡顿,这是多美好的憧憬。就跟故事中那个老板的最初想法一样美好。

自己一开始写多线程的时候,看了概念和文章,着手尝试的时候,但还是遇到了故事中那位老板的困惑。所以,我最后解决的方法,也跟这位老板一样:员工干自己的事,没有必要别总占用老板的时间。

关于耗时

各种I/O最耗时。比如读写磁盘,重绘界面,网络连接等。本文以更新界面为例说明。单线程的时候,一般有个单独的方法来加载界面。这是需要多线程处理的地方。

关于基础概念

进程、线程、时间片、异步、委托、代理……我建议不要往里钻,不结合实践可能一看就会,一做就废。所以这点——略!

我们只要知道,所谓UI线程,就是主进程这条线,初始化界面的线程,如果不用多线程的话,平时写的程序都是单线程的,就一个UI线程。

关于线程池

如果可能,尽量不要频繁创建和销毁线程,比较消耗资源。利用线程池。(现在硬件水平都还可以,如果是硬件资源非常宝贵的平台,恐怕从定义变量和数组就要考虑资源分配的问题了。)

如果一时理解不了,无视就行了,new thread一样用,先看到效果,再说优化。一位有名的讲师说过,任何成功的作品,都不是一次成型的,逐渐迭代完善是必然的。

关于invoke

原则:不能跨线程访问控件,要用委托和invoke。相当于故事中的财务报销制度,原则就是没有老板批准,员工不得擅自动用公司资金。

但网上的例子也仅仅是例子,真用起来照样卡。比如我们把UI线程里加载界面的方法函数,新建一个线程去异步回调它。其实用处不大的。其实后来发现这是一种混沌的想法。

sleep:有人建议大循环时不时用一下Thread.sleep(0),阻塞当前进程,让出时间片给UI线程是吧?光写这个没用的,因为当前线程太耗时了,总是刚让权就拿回,系统让它抢占,所以没戏。sleep多少秒都不行。

DoEvents:有人建议用Task.Delay()或者Application.DoEvents()取代sleep,我试过,DoEvents最明显,一是效率低,二是没解决实际问题,当处理耗时操作的时候,主界面是可以响应操作不死机,但是它会阻塞这个耗时线程,比如你按住滚动条,以为它滚动的同时加载过程还是继续走是吧?不行的,它就暂停了,你松开鼠标别动主界面它才继续。所以不建议用。

BackGround:甭想,没用的,这不是重点。

BeginInvoke:这是重点,主要看怎么用。先看看官方的解释:“在创建控件的基础句柄所在线程上异步执行委托。”这里面,所谓“在创建控件的基础句柄所在线程”也就是UI线程。就是不管在哪用invoke都是临时切换到UI线程来执行的。相当于员工要申请资金,必须回公司找老板办理。

所以,需要更新界面的时候,想当然新建一个线程,通过委托和invoke去调用它的入口方法,等于整个业务还是在UI线程执行的,所谓的新线程等于多此一举。我之前说这是混沌的想法就在于此了。相当于员工为了遵守财务制度,时刻守着老板干活,等于白招了这个员工。所以就有了解决思路:

1,这个用于更新界面的耗时方法,我们希望它在子线程中执行,不能有直接更新控件的操作(不止一次强调,这是原则,需要使用委托异步回调)。有关控件操作的部分要简化,减少跟控件的互动,尽量高效处理(其实单线程也该如此的)。而且要提出来做第2步。员工需要规范化自己的工作流程,提高效率。

2,为每一个与控件互动的操作,单独写一个方法,以便调用。切记,这个单独的方法用于更新控件,也就是必然是在UI线程中执行的。这是原则。员工需要提炼有关财务审批的环节。

3,在耗时方法中,凡是涉及到操作控件的地方,用BeginInvoke方法去异步调用上面第2步中UI线程中的方法。员工必要的时候才去找老板审批。 

4,除了上面第3步中的地方,这个耗时方法的执行进度,与UI线程是无关,直接放在新线程中执行就好了。员工干活,没必要的时候不用总麻烦老板。

执行结果终于达到效果,无论这个耗时操作多慢,主界面一点都不卡,可以拖动窗口,可以最大化最小化,可以操作滚动条,可以点击其它按钮,当然也可以点某个按钮终止当前耗时操作……相当于老板终于有时间处理其它事务,员工可以独立工作了。

然后就可以做我们想做的了。比如,执行耗时操作时,不希望那些按钮可以点,要在合适的时机控制它的Enable属性。这个要写在子线程里,用invoke去调用,写在主UI线程没用的。因为我们已经实现UI线程的完全自由了,它既然已经新开一个线程去干复杂的事了,那就只能等那个线程执行完才能恢复Enable状态。所以写在子线程里是比较合理的。

下面我把我的代码贴出来,这是我写的,整理孩子照片用的小程序,加载过程用了我说的方法,关键地方绿字做了说明,效果如期而至。不对的地方多指正。

最后给一个建议:各种命名规则见名知意不用说了,主要是方法的命名,凡是子线程里执行的方法,我都加了Thread_前缀,我觉得这有助于梳理思路,多线程编程,千万别乱。

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

C#多线程加载控件界面卡死的解决 的相关文章

随机推荐

  • 嵌入式linux系统下无法解析域名问题

    ping localhost ping bad address 39 localhost 39 ping www baidu com ping bad address 39 www baidu com 39 存在 etc hosts etc
  • tableau server在centos7.6上安装记录

    tableau server在centos7 6上安装记录 1 官网2 准备工作3 添加2个账号用于tableau server 管理员4 安装Tableau Server软件包 环境说明 xff1a tableau server 2019
  • Ubuntu-20.04默认进入字符界面

    因为需要多开虚拟机 xff0c 所以希望Linux默认进入字符界面 随着版本更新 xff0c 老的方法已经不好用了 sudo systemctl span class token function set span span class t
  • android uvccamera 编译

    1 NDK报错 xff1a Process 39 command 39 D SDK ndk bundle ndk build cmd 39 39 finished with non zero exit value 2 解决方法 ndk版本过
  • Android viewBinding让你告别findViewById和ButterKnife

    很久没有更新博客了 xff0c 不是因为别的 xff0c 就是懒 今天要分享的一个新技术 xff0c 从此告别定义一大串的UI控件变量 xff0c 再也不用写findViewById xff0c 也不需要依赖ButterKnife和写一堆
  • 编译OpenCV 4.7.0 无法解析的外部符号 cv::xfeatures2d::VGG::getDefaultName 问题解决

    最近做特征匹配 xff0c 需要用到xfeatures2d中的特征 xff0c 源码编译OpenCV 4 7 0及opencv contrib 4 7 0中的xfeatures2d模块 xff0c 在Visual Studio 2019中编
  • 记一次在Taro开发的微信小程序中使用lottie动画的经验

    前景提要 最近在做公司项目的时候 xff0c 看到移动端开发用的小图标有动态效果 xff0c 非常好玩了解到是使用lottie进行实现的 xff0c 这个东西以前有看到过对应的插件库 xff0c 但是一直没有时间做研究 xff0c 趁着这个
  • JDB调试Android程序(通过JDB进行代码注入)

    前言 最近在做一些安卓安全相关的事情 xff0c 就看到了一个通过动态调试进行代码注入的一个概念 xff0c 收益匪浅 xff0c 原来好多东西还能这么玩的 闲言少絮 xff0c 开始正式行动 漏洞检查 由于我这边是做的关于安卓安全相关的事
  • [2019.12.20]strncpy发生stack corruption detected(-fstack-protector)栈溢出

    代码 char line MAX 61 0 strncpy line pBeginObj ptemp pBeginObj 43 1 log如下 解释 char strncpy char dest const char src int n 把
  • libmng.so.1: cannot open shared object file: No such file or directory

    span class token function sudo span span class token function ln span s usr lib x86 64 linux gnu libmng so 2 usr lib x86
  • 企业级数据模型主题域模型划分( IBM-FSDM)

    一 前言 如何构建主题域模型原则是构建企业级数据仓库重要的议题 xff0c 最好的路径就是参照成熟的体系 IBM金融数据模型数据存储模型FSDM xff0c 是金融行业应用极为广泛的数据模型 xff0c 可以作为我们构建企业级数据仓库主题域
  • 关于编程学习上的一些感悟——不忘初心

    序 今天无意中看到以前一起开发过的同学写的技术文章 xff0c 了解到了更多在blog和github以及一些技术交流论坛上面非常活跃 回过头来看看自己 xff0c 好像依然停留在以前的样子 xff0c 似乎与真正在踏实学技术差距好像很大了
  • CentOS下ns-3安装教程

    首先 xff0c 安装ns 3时最好不要使用root权限 xff0c 普通用户安装即可 xff0c 否则后来要找文件会比较麻烦 一 安装依赖软件包 首先安装依赖软件包 根据官网 xff08 https www nsnam org wiki
  • 生产者-消费者模型

    文章来自https github com NieJianJian AndroidNotes xff0c 内容将持续更新 xff0c 欢迎star 一 前言 生产者消费者模式并不是GOF提出的23种设计模式之一 xff0c 23种设计模式都是
  • JAVA 多线程解决高并发、超时线程池耗尽问题

    第一类 问题 项目中遇到了 创建20个固定线程的线程池 在测试环境 多线程如果高并发的调用都没出现问题 但是在实际的项目中 出现了线程池内线程超时等待并将池内的线程耗尽 导致其它的程序走到多线程调用时候出现了执行慢 线程无法执行问题 问题原
  • 31_谈谈你对线程安全的理解?(重点)

    如果这个是面试官直接问你的问题 xff0c 你会怎么回答 xff1f 一个专业的描述是 xff0c 当多个线程访问一个对象时 xff0c 如果不用进行额外的同步控制或其他的协调操作 xff0c 调用这个对象的行为都可以获得正确的结果 xff
  • MariaDB 数据类型

    MariaDB 数据类型 数字数据类型 MariaDB支持的数字数据类型如下 类型描述TINYINT此数据类型表示落入 128到127的有符号范围内的小整数 xff0c 以及0到255的无符号范围 BOOLEAN此数据类型将值0与 fals
  • DBSCAN算法(python代码实现)

    DBSCAN 上次学了kmeans基于划分的方法 xff0c 这次学一个基于密度的聚类算法 xff1a DBSCAN xff08 Density Based Spatial Clustering of Applications with N
  • vs2022(缺少MFC,无法新建项目,控件无法添加事件)的解决

    最近下载安装了最新的vs2022社区版 xff0c 想着把之前的c 43 43 项目能够兼容 xff0c 于是遇到了一些列问题 缺少MFC xff0c 无法新建项目 xff0c 控件无法添加事件 这里首先要吐槽一下 xff1a 也许是我电脑
  • C#多线程加载控件界面卡死的解决

    先听一个故事 xff1a 有一个老板忙不过来 xff0c 于是招一个员工去负责某些事务 这样老板就可以腾出时间处理其它事 后来发现员工干不下去 xff0c 原因是干活需要花费 xff0c 没有老板的认可 xff0c 财务不给批钱 这是原则