几个月前做的一个软件里想添加一个天气预报功能, 也就是利用了一下Google Weather的接口: http://www.google.com/ig/api?hl=zh-cn&weather=某某市,某某省 , 效果也达到了.
不忘书中所讲: 耗时操作, 且非计算密集型任务, 最好使用异步方法. 根据Anders Hejlsberg的视频中演示的那样, 我写出下面一段代码, 也是很多人拿来演示异步的经典写法:
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对应到某个具体的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;