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