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下載。

轉載于:https://www.cnblogs.com/DreamRecorder/p/10823398.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/393819.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/393819.shtml
英文地址,請注明出處:http://en.pswp.cn/news/393819.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

iOS------App之間傳遞數據的幾種方式

UIDocumentInteractionController UIActivityViewController Shared Keychain Access Custom URL Scheme Web Service iCloud API UIPasteboard 參考 http://enharmonichq.com/sharing-data-locally-between-ios-apps/http://stackoverflow.com/questions/9425706/share-data-b…

了解如何使用Vue.js CLI

Interested in learning Vue.js? Get my ebook at vuehandbook.com有興趣學習Vue.js嗎&#xff1f; 在vuehandbook.com上獲取我的電子書 One of them is the Vue Command Line Interface (CLI).其中之一是Vue命令行界面(CLI)。 Note: There is a huge rework of the CLI goin…

rabbitmq學習——隊列

public class Send { public static final String routingKey "wuqidi_task_durable"; /*工作隊列 也叫任務隊列 目的是將任務發送到隊列中 由工作者進行處理 在后臺的多個工作者中 任務是共享的*/ public static void main(String[] args) throws Exception{ Conne…

python 戰艦_簡單Python戰艦

我最近開始學習python&#xff0c;并決定嘗試制作我的第一個項目。我正在嘗試做一個戰艦游戲&#xff0c;隨機放置兩個3塊長的船在一塊板上。但效果不太好。我為2號飛船做了一個臨時的循環&#xff0c;它應該檢查一下旁邊的兩個空間是否空閑&#xff0c;然后在那里建立自己的空…

leetcode面試題 08.12. 八皇后(回溯)

設計一種算法&#xff0c;打印 N 皇后在 N N 棋盤上的各種擺法&#xff0c;其中每個皇后都不同行、不同列&#xff0c;也不在對角線上。這里的“對角線”指的是所有的對角線&#xff0c;不只是平分整個棋盤的那兩條對角線。 注意&#xff1a;本題相對原題做了擴展 示例: 輸…

linux 進入redis 數據庫,Linux下Redis數據庫的安裝方法與自動啟動腳本分享

安裝Redis(1) 下載Rediswget http://redis.googlecode.com/files/redis-2.2.11.tar.gztar xzvf redis-2.2.11.tar.gz(2) 編譯并安裝Redismake && make install(3) 復制并修改配置文件cp redis.conf /etc/redis.confvi /etc/redis.conf注意修改以下幾項&#xff1a;daem…

Flutter 36: 圖解自定義 View 之 Canvas (三)

小菜繼續學習 Canvas 的相關方法&#xff1a; drawVertices 繪制頂點 小菜上次沒有整理 drawVertices 的繪制方法&#xff0c;這次補上&#xff1b;Vertice 即頂點&#xff0c;通過繪制多個頂點&#xff0c;在進行連線&#xff0c;多用于 3D 模型中&#xff1b; drawVertices 包…

sphinx 項目根目錄_如何使用Sphinx工具記錄Django項目

sphinx 項目根目錄I recently visited a company where I had a nice talk with one of its employees. We talked about technology and programming. Then we touched the subject of project documentation. Specifically how React does it automatically but Django doesn…

程序員必知之浮點數運算原理詳解

導讀&#xff1a;浮點數運算是一個非常有技術含量的話題&#xff0c;不太容易掌握。許多程序員都不清楚使用操作符比較float/double類型的話到底出現什么問題。 許多人使用float/double進行貨幣計算時經常會犯錯。這篇文章是這一系列中的精華&#xff0c;所有的軟件開發人員都應…

axure選中后橫線切換_3、開關狀態切換 —— Axure實用交互

寫在開頭:開關的制作在幾乎所有原型設計中都會用到&#xff0c;所以美觀自然的交互開關可以給你的原型設計加分不少。本次開關設計主要用到的是邏輯為&#xff1a;選中狀態的切換首先&#xff0c;來看一下演示動畫開始原型設計一、創建元件首先需要打開Axure軟件&#xff0c;并…

Django框架——模型(數據庫操作)

-- models.py-- ORM(object-relation mapping) 實現數據模型與數據庫的解耦&#xff1b;# 對象&#xff0c;關系&#xff0c;映射&#xff1b;1.根 據對象的類型生成表結構&#xff1b;2.將對象、列表的操作&#xff0c;轉換為sql語句&#xff1b;3.將sql查詢到的結果轉換為對象…

leetcode140. 單詞拆分 II(回溯+記憶化)

給定一個非空字符串 s 和一個包含非空單詞列表的字典 wordDict&#xff0c;在字符串中增加空格來構建一個句子&#xff0c;使得句子中所有的單詞都在詞典中。返回所有這些可能的句子。 說明&#xff1a; 分隔時可以重復使用字典中的單詞。 你可以假設字典中沒有重復的單詞。 …

#loj 3058 [HNOI2019] 白兔之舞

單位根反演思博題 模數是亂給的記得整個任意模數ntt k為p-1的約數意味著一定存在k次單位根&#xff0c;設g是p的原根則\(w_{k}^{1}g^{\frac{k-1}{p}}\) 既然k次單位根存在自然考慮單位根反演了 設\(f(i)\)表示跳了i步并且停在了第二維為y的頂點的方案數 設\(st\)表示初始向量而…

標桿徐2018 Linux自動化運維實戰,標桿徐2018 Linux自動化運維系列⑦: SaltStack自動化配置管理實戰...

結合企業自動化集群場景講解&#xff0c;輕松玩轉SaltStack自動化配置管理工具第1章 SaltStack基礎應用SaltStack安裝SaltStack認證Saltstack遠程執行SaltStack配置管理第2章 SaltStack數據系統SaltStack數據系統-Grains 客戶端向服務端發送狀態SaltStack數據系統-paiil 服務…

JS 對象引用問題

var a {n:1}; var b a; a {n:2}; a.x a ;console.log(a.x);console.log(b.x); var a {n:1}; var b a; a.x a {n:2}; console.log(a.x);console.log(b.x); 這兩個問題主要理解兩點就很簡單了。 對象是引用類型&#xff0c;改變賦值只是改變指針的引用。運算符相當于改變…

工程代碼_Egret開發筆記(二)基礎工程代碼閱讀

代碼目錄結構在Egret Wing中打開上一節中我們創建的項目工程&#xff0c;查看代碼目錄結構&#xff0c;Forward在如下圖中標記了各個目錄的及關鍵文件的用途。代碼閱讀理解接下來我們從web入口一步一步閱讀初始代碼。首先打開index.html文件&#xff0c;我們看到index文件內容如…

知曉云助力小程序開發

小程序開發遇到瓶頸雖然騰訊提供了小程序解決方案&#xff0c;https://cloud.tencent.com/solution/la。但是對于普通開發者或者小企業的開發人員來說&#xff0c;購買域名&#xff0c;網站備案、部署SSL證書&#xff0c;安裝會話服務器。業務邏輯上要使用數據庫&#xff0c;緩…

leetcode131. 分割回文串(回溯)

給定一個字符串 s&#xff0c;將 s 分割成一些子串&#xff0c;使每個子串都是回文串。 返回 s 所有可能的分割方案。 示例: 輸入: “aab” 輸出: [ [“aa”,“b”], [“a”,“a”,“b”] ] 代碼 class Solution {List<List<String>> stringListnew ArrayList…

Cracer滲透-windows基礎(系統目錄,服務,端口,注冊表)

系統目錄C:\Windows\system32\config\SAM (保存系統密碼) 無法正常修改&#xff0c;可以進入PE系統進行修改&#xff08;先備份在清空&#xff09;利用結束后&#xff0c;再將之前備份的恢復C:\Windows\System32\drivers\hosts&#xff08;域名解析文件&#xff09;hosts欺騙&a…

java--xml文件讀取(SAX)

SAX解析原理&#xff1a; 使用Handler去逐個分析遇到的每一個節點 SAX方式解析步奏&#xff1a; 創建xml解析需要的handler&#xff08;parser.parse(file,handler)&#xff09; package com.imooc_xml.sax.handler;import java.util.ArrayList;import org.xml.sax.Attributes…