需求:在silverlight用戶界面上使用計時器定時刷新數據。
在 Silverlight 中的 DispatcherTimer 的 Tick 事件 中使用異步請求數據時,會出現多次請求的問題,以下是ViewModel的代碼,看樣子沒什么問題:
using System; using System.Net; using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Windows.Threading; namespace AsyncTest.ViewModel { public class MainPageViewModel : NotificationBase { private int count; public int Count { get { return count; } set { this.count = value; base.OnPropertyChanged("Count"); } } protected WcfService.Service1Client client = null; private DispatcherTimer timer = null; protected int Interval = 1; public MainPageViewModel() { client = new WcfService.Service1Client(); timer = new DispatcherTimer(); timer.Interval = new System.TimeSpan(0, 0, Interval); timer.Tick += timer_Tick; timer.Start(); } private void timer_Tick(object sender, EventArgs ee) { client.GetDataAsync(); client.GetDataCompleted += ((s, e) => { this.Count++; }); } } }
然而,結果並不是我們預期的那樣,每次請求成功后,Count會以這樣的數列進行累加:1 3 6 10 15 21 。
經調試三天,排除在View層對ViewMode進行了多次初始化使Timer多次創建實例的可能,其他各種情況都排除后,最終把問題鎖定在Tick的方法體。
后來又經過再三調試,終於知道特么的問題在哪了,卧槽我艹,允許我爆一句粗口,搞了兩三天,媽蛋,才發現問題這么簡單,仔細觀察代碼,可以看出這是基於事件的異步編程模型。
問題:在Tick的方法里,client.GetDataCompleted+=((s,e)=>{//do something}); ,那么每次到了Tick執行周期的時候都會"+=" 一次事件,所以給了我們多次請求的錯覺,實際是請求了一次,只是多次執行了Completed事件而已。
這就是為什么Count的結果不是我們預期的 1 2 3 4 5 6 ... ,修改的代碼如下:
private void timer_Tick(object sender, EventArgs ee) { client.GetDataAsync(); client.GetDataCompleted += client_GetDataCompleted; } void client_GetDataCompleted(object sender, WcfService.GetDataCompletedEventArgs e) { //移除該事件,防止下次Tick事件執行時再次注冊 client.GetDataCompleted -= client_GetDataCompleted; this.Count++; }
將Completed的事件單獨提出來,在每次進入到Completed后,使用 "-=" 取消注冊該事件,再次進入到Tick的時候,重新請求,重新綁定就OK了,也可以直接在ViewModel的構造函數里綁定Completed事件,每次Tick里只請求client.GetDataAsync(),不需要在進行+=client_GetDataCompleted ,兩種方法皆可。
注意:實際項目中用到的是client_GetDataCompeleted事件的e.Result,這里是為了方便調試寫的一個Demo,使用Count的目的是為了監測Compeleted到底執行了幾次。
心得:每次遇到問題,不要急躁,可以由復雜到簡單,若不知問題在哪可使用排除法將問題逐一排除,最后查找到問題所在,仔細分析,不斷調試,找到問題根本,得到解決。
完!