通过实例代码理解WPF的Dispatcher
Dispatcher提供用于管理線程工作項(xiàng)隊(duì)列的服務(wù)。可以理解為消息隊(duì)列,只是其中保存的是委托,而不是簡單的windows消息。Dispatcher通常用來使我們的程序界面對(duì)于用戶的操作響應(yīng)更加迅速,通常用來更新UI,例如一個(gè)進(jìn)度條。例如一個(gè)耗時(shí)操作,我們不想讓使用者等得太著急,于是我們想顯示一個(gè)進(jìn)度條。
最直接的方法可能是在一個(gè)循環(huán)中更新,如以下這個(gè)錯(cuò)誤的代碼:
? ? ? ? ? ? ProgressBar1.Minimum = 0;ProgressBar1.Maximum = short.MaxValue; //模擬一個(gè)耗時(shí)計(jì)算
ProgressBar1.Value = 0;
while (ProgressBar1.Value < ProgressBar1.Maximum)
{
this.ProgressBar1.Value += 1;
}
然而直到循環(huán)結(jié)束,我們的界面都得不到更新,處于停止響應(yīng)狀態(tài)。我們利用Dispatcher成功的解決了這個(gè)問題:
ProgressBar1.Minimum = 0;ProgressBar1.Maximum = short.MaxValue;
ProgressBar1.Value = 0;
while (ProgressBar1.Value < ProgressBar1.Maximum)
{
Dispatcher.Invoke(new Action(() =>
{
this.ProgressBar1.Value += 1;
}),System.Windows.Threading.DispatcherPriority.Background);
}
參考WPF的線程模型知道,WPF使用呈現(xiàn)(render)線程隱藏在后臺(tái)負(fù)責(zé)應(yīng)用程序的呈現(xiàn),也就是界面的更新;而UI線程負(fù)責(zé)管理UI,上面的代碼就運(yùn)行在UI線程中,何謂管理UI,我的理解是可以改變UI元素的屬性,例如ProgressBar的Value值,也就是可以操作UI元素的意思。遺憾的是我們的UI線程不能通過代碼直接調(diào)用呈現(xiàn)線程讓其更新界面。
我們?cè)撛趺蠢斫庖陨系拇a呢?Dispatcher.Invoke的作用是將一個(gè)委托消息加入到消息隊(duì)列中,這個(gè)消息隊(duì)列中的消息是有優(yōu)先級(jí)的,優(yōu)先級(jí)是由DispatcherPriority這個(gè)枚舉確定的。同時(shí)Invoke是一個(gè)同步調(diào)用,因此是否也是通知Render線程開始刷新我們的界面呢?我們的while其實(shí)是在等待Render線程執(zhí)行完畢后,才繼續(xù)執(zhí)行,這令我們想到了線程的同步機(jī)制。而代碼1的情況,因?yàn)闆]有給Render線程任何通知,也就難怪不刷新了。由此得到的結(jié)論就是:Render線程何時(shí)開始工作其實(shí)是基于消息隊(duì)列中的特殊消息的,例如我們的Dispatacher.Invoke產(chǎn)生的消息(肯定不是所有的消息,因?yàn)槟銊?dòng)一下鼠標(biāo),界面不會(huì)刷新)。DispatcherPriority.Background指示我們的消息的優(yōu)先級(jí)低于Normal,這樣我們的UI線程就能停下來,讓Render線程的消息優(yōu)先得到執(zhí)行。那照你這么說,與我們Dispatcher中排隊(duì)的是什么樣的委托沒有關(guān)系了?只是起到一個(gè)UI線程暫停,同時(shí)通知Render線程工作的目的?是的,下面的代碼能夠到達(dá)同樣的效果:
ProgressBar1.Minimum = 0;ProgressBar1.Maximum = short.MaxValue;
ProgressBar1.Value = 0;
while (ProgressBar1.Value < ProgressBar1.Maximum)
{
this.ProgressBar1.Value += 1;
Dispatcher.Invoke(new Action(() =>
{
}),System.Windows.Threading.DispatcherPriority.Background);
}
我們排隊(duì)了一個(gè)空委托,照樣能喚醒Render線程。
我覺得這樣的理解已經(jīng)足夠,沒有必要請(qǐng)出Windbg,繼續(xù)深挖下去(其實(shí),那不是我所擅長的領(lǐng)域:))。這個(gè)推測(cè)足以幫助我們改進(jìn)代碼,怎么,需要改進(jìn)嗎?如果我們的推測(cè)正確的話,那么,以上代碼雖然使我們的用戶界面得到了及時(shí)的刷新,但同時(shí)也降低了性能。原因很簡單:頻繁的線程切換。UI線程和Render線程在頻繁的切換,以換取界面的及時(shí)刷新。short.MaxValue其實(shí)只有32767,做一個(gè)3萬多次的循環(huán)加1,卻用了20多秒!你不覺得驚訝嗎? 改進(jìn)方法也很簡單:減少Dispatcher.Invoke的調(diào)用,降低線程切換頻率。如下代碼只需要352毫秒:
ProgressBar1.Minimum = 0;ProgressBar1.Maximum = short.MaxValue ;
ProgressBar1.Value = 0;
double m = short.MaxValue / ProgressBar1.ActualWidth;
int n = 0;
while (ProgressBar1.Value < ProgressBar1.Maximum)
{
this.ProgressBar1.Value += 1;
n += 1;
if (n >= m)
{
Dispatcher.Invoke(new Action(() =>
{
}), System.Windows.Threading.DispatcherPriority.Background);
n = 0;
}
}
這是多少倍的性能提升啊!同時(shí)也證明了我們的推測(cè)是正確的!看來,有時(shí)遇到不理解的問題,編寫測(cè)試代碼來驗(yàn)證一下是一個(gè)不錯(cuò)的選擇。
------------------------2010-3-28更新---------------------------------------------
以上代碼為什么不使用BeginInvoke?
Invoke和BeginInvoke一個(gè)是同步調(diào)用,一個(gè)是異步調(diào)用。如果在While中進(jìn)行異步調(diào)用,由于不等調(diào)用完成就循環(huán)下一次了,循環(huán)結(jié)束條件始終得不到滿足,造成死循環(huán)了。如果非要BeginInvoke的話:
ProgressBar1.Minimum = 0;ProgressBar1.Maximum = short.MaxValue;
ProgressBar1.Value = 0;
while (ProgressBar1.Value < ProgressBar1.Maximum)
{
Dispatcher.BeginInvoke(new Action(() =>
{
this.ProgressBar1.Value += 1;
}), System.Windows.Threading.DispatcherPriority.Background).Wait();
}
這樣又變成了一個(gè)同步,沒有得到性能的提升。
使用一個(gè)新的線程:
我驚奇的發(fā)現(xiàn)以下代碼,使用一個(gè)新的Thread。不但不需要設(shè)置隊(duì)列的優(yōu)先級(jí)為Background,而且運(yùn)行時(shí)間大大縮短,只需要2秒多:
ProgressBar1.Minimum = 0;ProgressBar1.Maximum = short.MaxValue;
ProgressBar1.Value = 0;
double value = ProgressBar1.Value;
double max = ProgressBar1.Maximum;
new Thread(() =>
{
while (value < max)
{
value += 1;
Dispatcher.Invoke(new Action(() =>
{
this.ProgressBar1.Value = value;
}));
}
}).Start();
看來我們代碼2的20多秒,不光浪費(fèi)在線程切換上。至于更深層次的原因留到以后遇到了再說吧!
轉(zhuǎn)載于:https://www.cnblogs.com/slmk/archive/2012/03/27/2418251.html
總結(jié)
以上是生活随笔為你收集整理的通过实例代码理解WPF的Dispatcher的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: E-mail 标准 SMTP POP3
- 下一篇: 金融数据公司发展趋势小探