WPF多线程UI更新——两种方法

WPF多线程UI更新——两种方法

前言

  在WPF中,在使用多线程在后台进行计算限制的异步操作的时候,如果在后台线程中对UI进行了修改,则会出现一个错误:(调用线程无法访问此对象,因为另一个线程拥有该对象。)这是很常见的一个错误,一不小心就会有这个现象。在WPF中,如果不是用多线程的话,例如单线程应用程序,就是说代码一路过去都在GUI线程运行,可以随意更新任何东西,包括UI对象。但是使用多线程来更新UI就可能会出现以上所说问题,怎么解决?本文章提供两个方法:Dispatcher(大部分人使用),TaskScheduler(任务调度器)。

 

问题再现

  可能有的WPF新手不懂这是什么情况,先来个问题的再现,再使用本文章的两个方法进行解决。

  为了演示方便,我使用了最简单的布局,一个开始按钮,三个TextBlock。按一下开始按钮,开一个后台线程随机得到一个数字,并且更新第一个TextBlock。再开另外一个后台线程得到另外一个数字,更新第二个TextBlock。第三个TextBlock处理同理。

  XAML代码:

<Window x:Class="UpdateUIDemo.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow" Height="130" Width="363"><Canvas><TextBlock Width="40" Canvas.Left="38" Canvas.Top="27" Height="29" x:Name="first" Background="Black" Foreground="White"></TextBlock><TextBlock Width="40" Canvas.Left="128" Canvas.Top="27" Height="29" x:Name="second" Background="Black" Foreground="White"></TextBlock><TextBlock Width="40" Canvas.Left="211" Canvas.Top="27" Height="29" x:Name="Three" Background="Black" Foreground="White"></TextBlock><Button Height="21" Width="50" Canvas.Left="271" Canvas.Top="58" Content="开始" Click="Button_Click"></Button></Canvas>
</Window>

  后台代码:

public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}private void Button_Click(object sender, RoutedEventArgs e){Task.Factory.StartNew(Work);}private void Work(){Task task = new Task((tb) => Begin(this.first), this.first);Task task2 = new Task((tb) => Begin(this.second), this.first);Task task3 = new Task((tb) => Begin(this.Three), this.first);task.Start();task.Wait();task2.Start();task2.Wait();task3.Start();}private void Begin(TextBlock tb){int i=100000000;while (i>0){i--;}Random random = new Random();String Num = random.Next(0, 100).ToString();tb.Text = Num;}}

    运行一下,在点击开始按钮的时候,得到了一个错误信息:

  果然不出所料,Begin函数是在后台线程执行的,tb这个TextBlock是前台UI线程的对象,所以无法在后台线程改变UI线程拥有的对象,很多有点经验的WPF程序员就会使用下面我要说的Dispatcher了!

 

问题解决

  方法一:Dispatcher

    1.把UI更新的代码放到一个函数中:

private void UpdateTb(TextBlock tb, string text){tb.Text = text;}

    2.使用Dispatcher,大家看修改后的Begin函数(红色内容):

private void Begin(TextBlock tb){int i=100000000;while (i>0){i--;}Random random = new Random();String Num = random.Next(0, 100).ToString();Action<TextBlock, String> updateAction = new Action<TextBlock, string>(UpdateTb);tb.Dispatcher.BeginInvoke(updateAction,tb,Num);}

  再运行一次程序,可以看到能正常显示了,并且不会出现假死现象。

  方法二:任务调度器(TaskScheduler)

    有很多任务调度器,在CLR Var C#中就提出了线程池任务调度器,I/O任务调度器,任务限时调度器等,调度器的职责就是负责任务的调度,调节任务执行。同步上下文任务调度器就是该方法二所使用的调度器,其作用是将所有任务都调度给应用程序的GUI线程。

public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}private readonly TaskScheduler _syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();private void Button_Click(object sender, RoutedEventArgs e){Task.Factory.StartNew(SchedulerWork);}private void SchedulerWork(){Task.Factory.StartNew(Begin, this.first).Wait();Task.Factory.StartNew(Begin, this.second).Wait();Task.Factory.StartNew(Begin, this.Three).Wait();}private void Begin(object obj){TextBlock tb = obj as TextBlock;int i = 100000000;while (i>0){i--;}Random random = new Random();String Num = random.Next(0,100).ToString();Task.Factory.StartNew(() => UpdateTb(tb, Num),new CancellationTokenSource().Token, TaskCreationOptions.None, _syncContextTaskScheduler).Wait();}private void UpdateTb(TextBlock tb, string text){tb.Text = text;}}

 

   结果展示:

    

总结

  任务调度器还有很多种,按照自己喜欢的方法来实现后台多线程更新UI。还有任务调度器也可以应用到Winform中。下面提供示例Demo下载。

                                   完整Demo下载

转载于:https://www.cnblogs.com/Jarvin/p/3756061.html

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

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

相关文章

二叉树的递归遍历

二叉树遍历一&#xff0c;什么是二叉树二&#xff0c;递归实现2.1 结点类描述2.2 三种递归2.2 测试一&#xff0c;什么是二叉树 在计算机科学中&#xff0c;二叉树是每个结点最多有两个子树的树结构。通常子树被称作"左子树"&#xff08;left subtree&#xff09;和&…

概率论的公理结构

样本点 一个随机事件出现的可能的结果叫做样本点。 类比平面几何&#xff0c;线、面、体也是由点组成的集合&#xff0c;研究的是点线面关系及性质&#xff0c;同样样本点也是组成事件&#xff08;集合&#xff09;的材料&#xff0c;是集合的基本元素&#xff0c;把这些样本…

python词云的简单使用

词云的生成所需库代码实现wordclod参数说明具体实现效果展示所需库 wordcloud, jieba, imageiowordcloud 词云库&#xff0c;用来统计文本文档里面出现的高频词汇&#xff0c;或者句子&#xff0c;以图片可视化的方式显示出来jieba库&#xff0c;分割中文的库&#xff0c;把较…

(一)Neo4j在Centos7虚拟机上的安装

1、什么是图数据库&#xff1f; 图数据库是基于数学里图论的思想和算法而实现的高效处理复杂关系网络的新型数据库系统。图形数据库善于高效处理大量的、复杂的、互连的、多变的数据。其计算效率远远高于传统的关系型数据库。图形数据库在社交网络、实时推荐、征信系统、人工智…

(二)Cypher语言常用方法举例

1、概述 “Cypher”是一个描述性的类Sql的图操作语言。相当于关系数据库的Sql&#xff0c;可见其重要性&#xff01;其语法针对图的特点而设计&#xff0c;非常方便和灵活。没有Join&#xff0c;是一大特点&#xff01;学好Cypher是学好Neo4j的关键&#xff0c;也是核心所在&a…

github 人像卡通化探索项目

把项目下载到本地 下载地址 https://github.com/minivision-ai/photo2cartoon安装依赖库 python 3.7 # 3.x版本都可 pytorch 1.4 tensorflow-gpu 1.14 # tesorflow 得是1.0版本&#xff0c;2.0版本语法部分改变&#xff0c;不然项目运行会出错 face-alignment dlibpytorch …

CVE-2013-3897漏洞成因与利用分析

CVE-2013-3897漏洞成因与利用分析 1. 简介 此漏洞是UAF&#xff08;Use After Free&#xff09;类漏洞&#xff0c;即引用了已经释放的内存。攻击者可以利用此类漏洞实现远程代码执行。UAF漏洞的根源源于对对象引用计数的处理不当&#xff0c;比如在编写程序时忘记AddRef或者多…

(三)Neo4j自带northwind案例--Cypher语言应用

0、概述 通过该案例&#xff0c;应用Cypher查询语言&#xff0c;感受Neo4j套路。官方的用此案例的用意&#xff1a; The Northwind Graph demonstrates how to migrate&#xff08;迁移&#xff09; from a relational database to Neo4j&#xff08;把一个负责的多表关系数据…

RDIFramework.NET 中多表关联查询分页实例

RDIFramework.NET 中多表关联查询分页实例 RDIFramework.NET 中多表关联查询分页实例 RDIFramework.NET&#xff0c;基于.NET的快速信息化系统开发、整合框架&#xff0c;给用户和开发者最佳的.Net框架部署方案。该框架以SOA范式作为指导思想&#xff0c;作为异质系统整合与互操…

(六)Neo4j综合项目

0、概述 本文以热播电视剧《人民的名义》中的人物关系为数据基础&#xff0c;抛开案例本身的内容&#xff0c;本项目的意义在于指出使用Neo4j数据库的一般流程是什么&#xff1f;包括数据的导入、操作、查询、展示&#xff0c;从而体会出与传统数据库相比Neo4j在处理图数据的巨…

过滤器filter,监听器listener

目录1. filter过滤器1.1 原理1.2 配置1.3 过滤掉脏话demo2. listener监听器2.1 作用2.2 ServletContextListener demo1. filter过滤器 作用:过滤servlet&#xff0c;jsp&#xff0c;js&#xff0c;css&#xff0c;图片对象&#xff0c;以及一切在服务器&#xff0c;客户端想访…

(一)elasticsearch6.1.1安装详细过程

1、配置java环境 检查java环境 满足elasticsearch6.1.1java环境要求&#xff1b; 2、安装ElasticSearch6.1.1 ①为es新生成用户、用户组 su root groupadd esgroup useradd ela -g esgroup -p 5tgbhu8[rootlocalhost fibonacci]# su ela Attempting to create directory /h…

使用jdk DOM,SAX和第三方jar包DOM4J创建,解析xml文件

xml的创建&#xff0c;解析1. 什么是xml文件1.1 什么是xml文件1.2 解析xml的方式&#xff0c;优缺点2. 使用dom操作xml文件2.1 使用dom创建xml文件2.2 使用dom解析xml文件2.3 使用dom对xml文件增删改3. 使用SAX解析xml文件4. 使用DOM4J操作xml文件4.1 使用DOM4J创建xml文件4.2 …

(二)ElasticSearch6.1.1 Python API

0、准备开启数据库 ① 关闭Linux防火墙&#xff0c;这个很重要&#xff0c;否则API总是报错连不上。 # 查看防火墙状态 firewall-cmd --state# 关闭防护墙 systemctl stop firewalld.service# 开启防火墙 systemctl start firewalld.service# 重启防火墙 systemctl restart f…

sqlite3数据库使用

SQLite简介 SQLite是一个软件库&#xff0c;实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite是一个增长最快的数据库引擎&#xff0c;这是在普及方面的增长&#xff0c;与它的尺寸大小无关。SQLite 源代码不受版权限制。 什么是sqlite SQLite是一…

(三)ElasticSearch的基本概念

0、面向文档 应用中的对象很少只是简单的键值列表&#xff0c;更多时候它拥有复杂的数据结构&#xff0c;比如包含日期、地理位置、另一个对象或者数组。 总有一天你会想到把这些对象存储到数据库中。将这些数据保存到由行和列组成的关系数据库中&#xff0c;就好像是把一个丰…

ajax下拉框省市级联动

目录效果sql数据前后台代码实现效果 初始访问页面 选中省会&#xff0c;自动刷新页面 sql数据 -- 省市联动数据CREATE TABLE PROVINCE (PID NUMBER PRIMARY KEY,PNAME VARCHAR(20) NOT NULL )SELECT * FROM PROVINCEINSERT INTO province VALUES (1, 北京市); INSERT I…