WPF多線程UI更新——兩種方法

前言

在WPF中配猫,在使用多線程在后臺(tái)進(jìn)行計(jì)算限制的異步操作的時(shí)候,如果在后臺(tái)線程中對(duì)UI進(jìn)行了修改杏死,則會(huì)出現(xiàn)一個(gè)錯(cuò)誤:(調(diào)用線程無法訪問此對(duì)象泵肄,因?yàn)榱硪粋€(gè)線程擁有該對(duì)象。)這是很常見的一個(gè)錯(cuò)誤淑翼,一不小心就會(huì)有這個(gè)現(xiàn)象腐巢。在WPF中,如果不是用多線程的話玄括,例如單線程應(yīng)用程序冯丙,就是說代碼一路過去都在GUI線程運(yùn)行,可以隨意更新任何東西遭京,包括UI對(duì)象胃惜。但是使用多線程來更新UI就可能會(huì)出現(xiàn)以上所說問題,怎么解決哪雕?本文章提供兩個(gè)方法:Dispatcher(大部分人使用),TaskScheduler(任務(wù)調(diào)度器)船殉。

問題再現(xiàn)

可能有的WPF新手不懂這是什么情況,先來個(gè)問題的再現(xiàn)斯嚎,再使用本文章的兩個(gè)方法進(jìn)行解決利虫。

為了演示方便,我使用了最簡(jiǎn)單的布局堡僻,一個(gè)開始按鈕糠惫,三個(gè)TextBlock。按一下開始按鈕钉疫,開一個(gè)后臺(tái)線程隨機(jī)得到一個(gè)數(shù)字硼讽,并且更新第一個(gè)TextBlock。再開另外一個(gè)后臺(tái)線程得到另外一個(gè)數(shù)字陌选,更新第二個(gè)TextBlock理郑。第三個(gè)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>

后臺(tái)代碼:

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;
        }
    }

運(yùn)行一下,在點(diǎn)擊開始按鈕的時(shí)候您炉,得到了一個(gè)錯(cuò)誤信息:

果然不出所料柒爵,Begin函數(shù)是在后臺(tái)線程執(zhí)行的,tb這個(gè)TextBlock是前臺(tái)UI線程的對(duì)象赚爵,所以無法在后臺(tái)線程改變UI線程擁有的對(duì)象棉胀,很多有點(diǎn)經(jīng)驗(yàn)的WPF程序員就會(huì)使用下面我要說的Dispatcher了!

問題解決

* 方法一:Dispatcher

1. 把UI更新的代碼放到一個(gè)函數(shù)中:

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

2. 使用Dispatcher,大家看修改后的Begin函數(shù)(紅色內(nèi)容):

    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);
    }

再運(yùn)行一次程序冀膝,可以看到能正常顯示了唁奢,并且不會(huì)出現(xiàn)假死現(xiàn)象。

* 方法二:任務(wù)調(diào)度器(TaskScheduler)

有很多任務(wù)調(diào)度器窝剖,在CLR Var C#中就提出了線程池任務(wù)調(diào)度器麻掸,I/O任務(wù)調(diào)度器,任務(wù)限時(shí)調(diào)度器等赐纱,調(diào)度器的職責(zé)就是負(fù)責(zé)任務(wù)的調(diào)度脊奋,調(diào)節(jié)任務(wù)執(zhí)行。同步上下文任務(wù)調(diào)度器就是該方法二所使用的調(diào)度器疙描,其作用是將所有任務(wù)都調(diào)度給應(yīng)用程序的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;
        }
    }

結(jié)果展示:

總結(jié)

任務(wù)調(diào)度器還有很多種,按照自己喜歡的方法來實(shí)現(xiàn)后臺(tái)多線程更新UI起胰。還有任務(wù)調(diào)度器也可以應(yīng)用到Winform中久又。下面提供示例Demo下載。

完整Demo下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末效五,一起剝皮案震驚了整個(gè)濱河市地消,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌火俄,老刑警劉巖犯建,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異瓜客,居然都是意外死亡适瓦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門谱仪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玻熙,“玉大人,你說我怎么就攤上這事疯攒∴滤妫” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)枚尼。 經(jīng)常有香客問我贴浙,道長(zhǎng),這世上最難降的妖魔是什么署恍? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任崎溃,我火速辦了婚禮,結(jié)果婚禮上盯质,老公的妹妹穿的比我還像新娘袁串。我一直安慰自己,他們只是感情好呼巷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布囱修。 她就那樣靜靜地躺著,像睡著了一般王悍。 火紅的嫁衣襯著肌膚如雪破镰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天压储,我揣著相機(jī)與錄音啤咽,去河邊找鬼。 笑死渠脉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瓶佳。 我是一名探鬼主播芋膘,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼霸饲!你這毒婦竟也來了为朋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤厚脉,失蹤者是張志新(化名)和其女友劉穎习寸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體傻工,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡霞溪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了中捆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸯匹。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖泄伪,靈堂內(nèi)的尸體忽然破棺而出殴蓬,到底是詐尸還是另有隱情,我是刑警寧澤蟋滴,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布染厅,位于F島的核電站痘绎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏肖粮。R本人自食惡果不足惜孤页,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尿赚。 院中可真熱鬧散庶,春花似錦、人聲如沸凌净。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冰寻。三九已至须教,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間斩芭,已是汗流浹背轻腺。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留划乖,地道東北人贬养。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像琴庵,于是被迫代替她去往敵國和親误算。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,180評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理迷殿,服務(wù)發(fā)現(xiàn)儿礼,斷路器,智...
    卡卡羅2017閱讀 134,665評(píng)論 18 139
  • Object C中創(chuàng)建線程的方法是什么庆寺?如果在主線程中執(zhí)行代碼蚊夫,方法是什么?如果想延時(shí)執(zhí)行代碼懦尝、方法又是什么知纷? 1...
    AlanGe閱讀 1,740評(píng)論 0 17
  • 曉榕一臉倦容地走進(jìn)辦公室,同事小周擦身而過导披,躲避得遠(yuǎn)遠(yuǎn)地去接開水屈扎,曉榕慢慢地走到位置上,一臉不在狀態(tài)地問另一同事怎...
    夕雁無邊閱讀 251評(píng)論 0 1
  • 在羅伯特·麥基的《故事》里撩匕,《走出非洲》是一個(gè)反諷的故事:一個(gè)生活在“我即我所擁有”這一倫理中的富家女鹰晨,為了男爵夫...
    微冷微冷閱讀 9,372評(píng)論 1 22