Visual Studio Async CTP

Visual Studio Async CTP

寫程式難免會遇到回應延遲的問題,回應延遲的原因很多,有可能是一次擷取的資料量太大,也有可能是網路的問題,若是以單一執行緒的方式撰寫程式碼,使用者大概就只能等待,就連取消的機會都沒有。

在過去若要處理這種問題是很麻煩的,具有UI的應用程式通常是單一執行緒(STA),因此要直接跨執行緒存取資料是不可行的,以WPF為例通常必須設定一個static變數讓多個執行緒共同存取,控制項所提供的Dispatcher.BeginInvoke方式進行該變數的存取...

使用過VS11 Beta的使用者或許知道,在新版的.NET 4.5中提供了一種async…await的非同步方法,稱為Visual Studio Async Framework,如果在Visual Studio 2010中要執行這種非同步作業必須先安裝Visual Studio Async CTP...

安裝完畢後在"我的文件"中會產生一個Microsoft Visual Studio Async CTP的資料夾,其中包含了許多sample範例,使用時只要先將C:\Users\使用者\Documents\Microsoft Visual Studio Async CTP\Samples下的AsyncCtpLibrary.dll加入參考即可使用!

以一個下載RSS內容的小程式為例,我可能寫一個方法執行RSS的下載與剖析...

void LoadRss(string uri)
        {
            System.Net.WebRequest myRequest = System.Net.WebRequest.Create(uri);
            System.Net.WebResponse myResponse = myRequest.GetResponse();
            System.IO.Stream rssStream = myResponse.GetResponseStream();
            System.Xml.XmlDocument rssDoc = new System.Xml.XmlDocument();
            rssDoc.Load(rssStream);
            System.Xml.XmlNodeList rssItems = rssDoc.SelectNodes("rss/channel/item");
            ObservableCollection<RssItem> items = new ObservableCollection<RssItem>();
            for (int i = 0; i < rssItems.Count; i++)
            {
                RssItem item = new RssItem();
                System.Xml.XmlNode rssDetail;
                rssDetail = rssItems.Item(i).SelectSingleNode("title");
                if (rssDetail != null)
                {
                    item.Title = rssDetail.InnerText;
                }
                else
                {
                    item.Title = "";
                }
                rssDetail = rssItems.Item(i).SelectSingleNode("author");
                item.Author = "";
                if (rssDetail != null)
                {
                    item.Author = rssDetail.InnerText;
                }
                else
                {
                    item.Author = "";
                }
                rssDetail = rssItems.Item(i).SelectSingleNode("pubDate");
                if (rssDetail != null)
                {
                    try
                    {
                        item.PublishDate = Convert.ToDateTime(rssDetail.InnerText).ToString("yyyy年MM月dd日");
                    }
                    catch
                    {
                        item.PublishDate = DateTime.Today.ToLocalTime().ToShortDateString();
                    }
                }
                else
                {
                    item.PublishDate = "";
                }
                rssDetail = rssItems.Item(i).SelectSingleNode("link");
                if (rssDetail != null)
                {
                    item.Link = rssDetail.InnerText;
                }
                else
                {
                    item.Link = null;
                }
                items.Add(item);
            }
            listRss.ItemsSource = items;

        }

這個程式基本上沒問題,但在第7行執行rssDXoc.Load()時可能會造成程式的瓶頸,因為載入RSS時是透過網際網路進行,因此在載入的同時會造成UI無法回應,如果我們改用Visual Studio Async 就可以解決這問題:

1.請把C:\Users\使用者\Documents\Microsoft Visual Studio Async CTP\Samples下的AsyncCtpLibrary.dll加入參考

2.加入System.Threading.Tasks命名空間

3.在原有的方法前加入async關鍵字

4.await TaskEx.Run(()=>要執行方法),如下第10行的委派方式

using System.Threading.Tasks;
.....
.....  
async void LoadRssAsync(string uri)
        {
            System.Net.WebRequest myRequest = System.Net.WebRequest.Create(uri);
            System.Net.WebResponse myResponse = myRequest.GetResponse();
            System.IO.Stream rssStream = myResponse.GetResponseStream();
            System.Xml.XmlDocument rssDoc = new System.Xml.XmlDocument();
            await TaskEx.Run(()=>rssDoc.Load(rssStream));         
            System.Xml.XmlNodeList rssItems = rssDoc.SelectNodes("rss/channel/item");
            ObservableCollection<RssItem> items = new ObservableCollection<RssItem>();
            for (int i = 0; i < rssItems.Count; i++)
            {
                RssItem item = new RssItem();
                System.Xml.XmlNode rssDetail;
                rssDetail = rssItems.Item(i).SelectSingleNode("title");
                if (rssDetail != null)
                {
                    item.Title = rssDetail.InnerText;
                }
                else
                {
                    item.Title = "";
                }
                rssDetail = rssItems.Item(i).SelectSingleNode("author");
                item.Author = "";
                if (rssDetail != null)
                {
                    item.Author = rssDetail.InnerText;
                }
                else
                {
                    item.Author = "";
                }
                rssDetail = rssItems.Item(i).SelectSingleNode("pubDate");
                if (rssDetail != null)
                {
                    try
                    {
                        item.PublishDate = Convert.ToDateTime(rssDetail.InnerText).ToString("yyyy年MM月dd日");
                    }
                    catch
                    {
                        item.PublishDate = DateTime.Today.ToLocalTime().ToShortDateString();
                    }
                }
                else
                {
                    item.PublishDate = "";
                }
                rssDetail = rssItems.Item(i).SelectSingleNode("link");
                if (rssDetail != null)
                {
                    item.Link = rssDetail.InnerText;
                }
                else
                {
                    item.Link = null;
                }            
                items.Add(item);
            }
            listRss.ItemsSource = items;

        }

要注意的是,async只能使用在無回傳型別的方法上,另外使用了Visual Studio Async 後,某些物件會增加擴充方法,例如:WebClient會有些以xxxTaskAsync的擴充方法,只要是回傳型別為Task或Task<T>的方法都可以使用await,若無就使用TaskEx.Run(delegate void)的方式即可!

AsyncWPF.rar(測試檔案下載)