Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 865|回复: 0

C#的WPF中使用多线程导致界面假死问题的解决

[复制链接]
  • TA的每日心情
    奋斗
    6 小时前
  • 签到天数: 803 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726482
    发表于 2021-4-13 12:27:37 | 显示全部楼层 |阅读模式

    某项目需要将实时传来的渔船数据进行数据可视化,我负责Windows客户端的卡顿优化,此处的卡顿指界面无响应。

    第一步是对客户端的行为的观察,观察卡顿发生的条件以及是否有规律。经过观察,客户端在网络良好的情况下卡顿4~6秒,网络较差的情况下更长,得出结论①卡顿与网络状况有关。在网络稳定的情况下观察卡顿发生的时间间隔,发现从开始卡顿到下一次开始卡顿间隔大概是20秒,得出结论②卡顿是周期性的。通过这两个结论可以进一步假设卡顿是由于周期性的网络请求导致的,网络良好的情况下响应时间短,卡顿也短,反之也成立。找到客户端的配置文件,发现该请求的确是20秒一词,假设成立。

    第二步进行抓包,进一步验证,由于本项目使用SOAP通过远程过程调用来实现请求和响应,一个service有多种方法,为了确定具体是哪个请求,抓包具体看。同样,当那个请求开始的时候,客户端开始卡顿,直到那个请求结束,客户端恢复了正常,已经可以确定就是那个请求引起的客户端卡顿。

    现在假设一种比较简单的情况,客户端没有使用多线程,在UI线程中请求网络,导致界面假死。遂翻看代码,代码中明晃晃的Thread thread = new Thread...,的确使用了多线程,但有些怪怪的,大致代码如下:

    Thread thread = new Thread(new ThreadStart(delegate {
    	this.Dispatcher.Invoke(new Action(() => {
    		// 获取控件数据并且构造参数param
    		...
    		// 请求网络
    		responseText = WebServiceHandler.ObjectWebServer.getObject(param);
    		// 解析响应数据
    		Object o = JsonTools<Object>.DeserializeList(responseText);
    		// 更新控件
    		...
    	}));
    }))
    thread.Start();
    

    只能看懂一部分,创建线程,然后使用delegate委托实际上是一个匿名方法传入ThreadStart中作为Thread的参数。this.Dispatcher.Invoke部分看不懂,不过大概是将一个Action作为一个委托传给Invoke方法。

    于是Google之,查到如下资料:

    In WPF, only the thread that created a DispatcherObject may access that object. For example, a background thread that is spun off from the main UI thread cannot update the contents of a Button that was created on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. The operation is added to the event queue of the Dispatcher at the specified DispatcherPriority. Invoke is a synchronous operation; therefore, control will not return to the calling object until after the callback returns.

    只有创建DispatcherObject对象的线程才能访问之,其他线程不可访问该对象,比如UI线程创建的对象其他后台线程就不可访问。那么如果想访问该UI线程创建的控件,就必须将一个访问该控件的方法委托给创建控件的UI线程的Dispatcher去执行,才能假借UI线程之手去访问控件。this.Dispatcher.Invoke就是使用UI线程的Dispatcher调用Invoke方法执行该委托,但是这个委托中使用了比较耗时的网络请求,也就是说该请求如果在Invoke方法中,就会占用UI线程的时间片去执行,即使放在了创建的线程中。

    解决的方法就是将访问控件的部分放入Invoke方法汇总,其他的耗时请求就在线程的方法中执行。然后这个问题就被轻易地解决了。

    这个问题产生的原因是写代码的师兄不明白如何创建线程,就去网上找到了一个创建线程的代码,并且炒上去,然后发现这个代码并不能访问看似全局数据的控件,就又去网上找代码,发现把方法放进this.Dispathcer.Invoke中就可以访问了,以为就万事大吉了。从这个例子中收获的教训是如果不完全明白代码的细节,那么尽可能先去了解,不然如果直接炒上去,那么后续完全可能产生意想不到的Bug,而且要花费更大的精力去弄明白这个用法,然后再去解决。

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-11-17 21:50 , Processed in 6.061632 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表