请求 msdn 上的一个页面计算页面大小
- static void Main(string[] args)
- {
- string url = "https://docs.microsoft.com/zh-cn/dotnet/core/api/system";
- try
- {
- webRequest request = WebRequest.Create(url);
- WebResponse response = request.GetResponse();
- using (var reader = new StreamReader(response.GetResponseStream()))
- {
- string text = reader.ReadToEnd();
- Console.WriteLine(FormatBytes(text.Length));
- }
- }
- catch (WebException e)
- {
- }
- catch (IOException e)
- {
- }
- catch (NotSupportedException e)
- {
- }
- }
- static string FormatBytes(long bytes)
- {
- string[] magnitudes = new string[] { "GB", "MB", "KB", "Bytes" };
- long max = (long)Math.Pow(1024, magnitudes.Length);
- var t1 = magnitudes.FirstOrDefault(magnitude => bytes > (max /= 1024)) ?? "0 Bytes";
- var t2 = ((decimal)bytes / (decimal)max);
- return string.Format("{1:##.##} {0}", t1, t2).Trim();
- }
Ctrl+F5 输出
闪烁两下后
这里对资源的请求都是同步的, 通俗易懂点就是一个步骤一个步骤的执行, 任何一个步骤耗时较长都会阻塞上下文线程 (这里就是主线程)
- static void Main(string[] args)
- {
- string url = "https://docs.microsoft.com/zh-cn/dotnet/core/api/system";
- Task task = WriteWebRequestSizeAsync(url);
- while (!task.Wait(100))
- {
- Console.Write(".");
- }
- }
- static async Task WriteWebRequestSizeAsync(string url)
- {
- try
- {
- WebRequest request = WebRequest.Create(url);
- WebResponse response = await request.GetResponseAsync();
- using (var reader = new StreamReader(response.GetResponseStream()))
- {
- string text = await reader.ReadToEndAsync();
- Console.WriteLine(FormatBytes(text.Length));
- }
- }
- catch (WebException)
- {
- }
- //省略了一些catch块
- }
这种写法在 MVC 中早就熟悉了, 但是原理确不是很清楚, 只知道这样不会阻塞 UI,async 方法会创建一个新的线程执行, await 会阻塞上下文线程, 一知半解隐藏着很可怕定时炸弹!
- static void Main(string[] args)
- {
- string url = "https://docs.microsoft.com/zh-cn/dotnet/core/api/system";
- Task task = WriteWebRequestSizeAsync(url);
- try
- {
- while (!task.Wait(100))
- {
- Console.Write(".");
- }
- }
- catch (AggregateException excetion)
- {
- excetion = excetion.Flatten();
- try
- {
- excetion.Handle(innerExcetion =>
- {
- ExceptionDispatchInfo.Capture(excetion.InnerException).Throw();
- return true;
- });
- }
- catch (WebException ex)
- {
- }
- //省略了一些catch块
- }
- }
- static Task WriteWebRequestSizeAsync(string url)
- {
- StreamReader reader = null;
- WebRequest request = WebRequest.Create(url);
- Task task = request.GetResponseAsync()
- .ContinueWith(antecedent =>
- {
- WebResponse response = antecedent.Result;
- reader = new StreamReader(response.GetResponseStream());
- return reader.ReadToEndAsync();
- })
- .Unwrap()
- .ContinueWith(antecedent =>
- {
- if (null != reader)
- reader.Dispose();
- string text = antecedent.Result;
- Console.WriteLine(FormatBytes(text.Length));
- });
- return task;
- }
这个写法是在没有 C#5.0 之前, 异步请求资源就是这么完成的, 乍一看非常复杂, 但是比较一下上面一种写法, 它似乎是思路清晰的
1.request.GetResponseAsync 创建一个任务等待 msdn 服务器的响应
2. 拿到这个响应后, 获得响应流, 将流转为字符串
3. 接下来是 Unwrap, 这个应该是最难理解的了, 实际上只有加上这句话以 ContinueWith 的思路写下去, 下一步就是直接拿 string 了
4. 最后流已经转为字符串了, 我们做个简单的计算就行了
- public static Task Unwrap(this Task> task);
从签名上来看, 它是一个 Task<Task<TResult>> 类型的拓展方法, 任务, 带返回值的任务... 晕了, 别急, 等下慢慢来
回到刚刚代码看看有什么端倪
1.GetResponseAsync()创建 (意思和返回值一样, 为了区分任务返回值) 一个任务, 具体类型 Task<WebResponse>, 这个任务结束返回一个 WebResponse
2. 第一个 ContinueWith 调用者是一个 Taske>, 形参是一个委托接受一个 Task
3. 现在问题来了, 我们得到的是一个 Task<Task<string>>, 我们可以通过调用两次 Result 获取这个 string, 但是在这个 ContinueWith 块中, 只能保证外层的 Task 是执行完成的, 所以第二个 Result 或阻塞上下文线程
- Task task = request.GetResponseAsync()
- .ContinueWith(antecedent =>
- {
- WebResponse response = antecedent.Result;
- reader = new StreamReader(response.GetResponseStream());
- return reader.ReadToEndAsync();
- })
- .ContinueWith(antecedent =>
- {
- var resTask = antecedent.Result;
- var resString = resTask.Result;
- if (null != reader)
- {
- string text = resString;
- Console.WriteLine(FormatBytes(text.Length));
- }
- });
4. 这个时候再回到 Unwarp(), 它就是脱去了外层的 Task, 得到的内层的任务上下文线程, 并把它作为延续任务的主线程
5. 这里取出的 Result 就是 string, 计算并输出
通过 ILSpy 反编译后可以逐步查到, Unwarp() 实际上就是保证了内层任务开始执行, 并返回一个内层任务的执行环境 (上下文线程)
来源: http://www.cnblogs.com/cheesebar/p/6553310.html