幾個月前做的一個軟體裡想添加一個天氣預報功能, 也就是利用了一下Google Weather的介面: http://www.google.com/ig/api?hl=zh-cn&weather=某某市,某某省 , 效果也達到了.
不忘書中所講: 耗時操作, 且非計算密集型任務, 最好使用非同步方法呼叫. 根據Anders Hejlsberg的視頻中示範的那樣, 我寫出下面一段代碼, 也是很多人拿來示範非同步經典寫法:
public void GetWeather(string city, string province) { var myRequest = (HttpWebRequest)WebRequest.Create("http://www.google.com/ig/api?hl=zh-cn&weather=" + city + "," + province); myRequest.BeginGetResponse(delegate(IAsyncResult ar) { var response = myRequest.EndGetResponse(ar); StreamReader weatherStream = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("gb2312")); string weatherString = weatherStream.ReadToEnd(); weatherStream.Dispose(); }, null); }
就是這樣的一個所謂非同步方法, 運行一下, 最短的時候花了3秒多才擷取到了weatherString, 很多時候甚至花了10秒多. 我就眼巴巴的看著程式在假死(Not Responding), 一邊看著My Code, 我不是非同步了麼? 非同步不是就是為了避免程式假死的麼? 目前來看程式似乎並沒有非同步.
於是開始找原因...也請教了不少人...也得到了一些可能的原因:
- 嫌疑1: 程式啟動並執行時候第一步在尋找DNS將google.com對應到某個具體的IP地址, 這項任務花費了不少時間.
事實上我Ping了google.com之後, 把google.com換成IP地址, 運行程式, 並沒有發現有什麼效果...
- 嫌疑2: 程式只實現了BeginGetResponse的非同步, 還有GetResponseStream等等之類的方法並沒有非同步.
但把GetResponseStream改成BeginGetResponseStream之後, 也沒有任何改觀.
- 嫌疑3: 系統在給這個WebRequest分配資源, 諸如WebRequest類, StreamReader之類的還算"比較大"的對象花費了時間.
想一想這些應該都是在快取上進行的, 不至於要花3秒, 10多秒吧?
這個問題還真不太好描述, 事實上後來做了一系列的測試, 測試發現只有第一次發出WebRequest看似不是非同步. 接下來繼續嘗試幾次發出WebRequest, 到獲得Response的時間就非常非常短. 為了找出究竟在哪個環節耗時比較厲害, 我寫了一個控制台程式來測試, 測試中我用了一個for迴圈, 連續發出5次同樣的WebRequest, 測試結果如下:
可以發現程式在第一次初始化WebRequest和第一次從發出請求到獲得響應消耗的時間最多! 昨天發現原來是代理(Proxy)的問題! MSDN中關於HttpWebRequest.Proxy屬性是這樣描述的:
本機電腦或應用程式設定檔可能指定使用預設代理。 如果指定了 Proxy 屬性,則 Proxy 屬性中的代理設定會重寫本機電腦或應用程式設定檔,並且 HttpWebRequest 執行個體將實用指定的代理設定。 如果設定檔中未指定代理並且未指定 Proxy 屬性,則 HttpWebRequest 類使用從本機電腦上的 Internet Explorer 中繼承的代理設定。 如果 Internet Explorer 中沒有代理設定,請求會直接發送到伺服器。
回到遇到的問題, 程式並沒有指定代理, 第一次啟動並執行時候, 程式會尋找IE中的代理, 如果沒有找到才會去直接存取伺服器, 這中間花費了不少時間. 要解決這個問題, 請在代碼中加上這麼一行:
xxRequest.Proxy = null;