同步 VS 异步
同步请求资源
请求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(, magnitudes.Length);
var t1 = magnitudes.FirstOrDefault(magnitude => bytes > (max /= )) ?? "0 Bytes";
var t2 = ((decimal)bytes / (decimal)max);
return string.Format("{1:##.##} {0}", t1, t2).Trim();
}
Ctrl+F5输出

闪烁两下后

这里对资源的请求都是同步的,通俗易懂点就是一个步骤一个步骤的执行,任何一个步骤耗时较长都会阻塞上下文线程(这里就是主线程)
使用C#5.0异步请求资源
static void Main(string[] args)
{
string url = "https://docs.microsoft.com/zh-cn/dotnet/core/api/system";
Task task = WriteWebRequestSizeAsync(url);
while (!task.Wait())
{
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会阻塞上下文线程,一知半解隐藏着很可怕定时炸弹!
TPL异步调用
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())
{
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.最后流已经转为字符串了,我们做个简单的计算就行了
Unwrap的奥义
public static Task<TResult> Unwrap<TResult>(this Task<Task<TResult>> task);
从签名上来看,它是一个Task<Task<TResult>>类型的拓展方法,任务,带返回值的任务...晕了,别急,等下慢慢来
回到刚刚代码看看有什么端倪
1.GetResponseAsync()创建(意思和返回值一样,为了区分任务返回值)一个任务,具体类型Task<WebResponse>,这个任务结束返回一个WebResponse

2.第一个ContinueWith调用者是一个Task<WebResponse>,形参是一个委托接受一个Task<WebReponse>,返回一个Task<string>(通过reader.ReadToEndAsync可以理解),返回值是一个Task没有问题,问题在它的泛型参数是什么,首先此处ContinueWith中的代码是在一个新的工作线程中运行的,我们把它想象成主线程(只是相对的一个环境),'主线程'要完成什么任务呢?,他要拿Task<WebResponse>的执行结果WebResponse(虽然这里可以肯定这个任务已经执行完成了,但是微软没有这么做),然后根据这个WebResponse在创建一个Task<string>,作为当前工作线程的返回值

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
通过ILSpy反编译后可以逐步查到,Unwarp()实际上就是保证了内层任务开始执行,并返回一个内层任务的执行环境(上下文线程)


同步 VS 异步的更多相关文章
- .NET同步与异步之相关背景知识(六)
在之前的五篇随笔中,已经介绍了.NET 类库中实现并行的常见方式及其基本用法,当然.这些基本用法远远不能覆盖所有,也只能作为一个引子出现在这里.以下是前五篇随笔的目录: .NET 同步与异步之封装成T ...
- 简述linux同步与异步、阻塞与非阻塞概念以及五种IO模型
1.概念剖析 相信很多从事linux后台开发工作的都接触过同步&异步.阻塞&非阻塞这样的概念,也相信都曾经产生过误解,比如认为同步就是阻塞.异步就是非阻塞,下面我们先剖析下这几个概念分 ...
- 同步与异步 & 阻塞与非阻塞
在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 一.同步 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用 ...
- TCP同步与异步,长连接与短连接【转载】
原文地址:TCP同步与异步,长连接与短连接作者:1984346023 [转载说明:http://zjj1211.blog.51cto.com/1812544/373896 这是今天看到的一篇讲到T ...
- .NET 同步与异步之封装成Task(五)
本随笔续接:.NET 实现并行的几种方式(四) 前篇随笔已经介绍了几种可以实现并发的方式,其中异步方法.是最简便的方式.而 异步方式是基于 Task 和 async修饰符和await运算符实现的. 换 ...
- 网络IO之阻塞、非阻塞、同步、异步总结
网络IO之阻塞.非阻塞.同步.异步总结 1.前言 在网络编程中,阻塞.非阻塞.同步.异步经常被提到.unix网络编程第一卷第六章专门讨论五种不同的IO模型,Stevens讲的非常详细,我记得去年看第一 ...
- python学习笔记-(十四)I/O多路复用 阻塞、非阻塞、同步、异步
1. 概念说明 1.1 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可 ...
- ASP.NET WebAPi(selfhost)之文件同步或异步上传
前言 前面我们讲过利用AngularJs上传到WebAPi中进行处理,同时我们在MVC系列中讲过文件上传,本文结合MVC+WebAPi来进行文件的同步或者异步上传,顺便回顾下css和js,MVC作为客 ...
- socket阻塞与非阻塞,同步与异步、I/O模型,select与poll、epoll比较
1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步: 所谓同步,就 ...
- WCF的同步和异步(以WPF连接为例)
2016-06-0711:05:44 在学习WCF时,学到WCF服务的同步和异步. 我理解的同步是: 当WCF服务是同步执行时,程序只有一条线程,代码只能按顺序一步一步来执行,当执行客户端/服务端某方 ...
随机推荐
- 简单加解密算法 - vb.net
Public Class Form1 Dim charAarray() As Char '加密 Private Sub Btn_En_Click(sender As System.O ...
- iOS 多线程NSThread理解与场景示例
NSThread是相对GCD和NSOperationQuene而言,比较轻量级的一种多线程处理方式. 但同时,它的弊端就是需要自己管理线程的生命周期,以及线程同步:而另外两种不需要自己管理. 常见方法 ...
- LoadRunner相关架构图
LoadRunner概览图: Lr架构图:
- RabbitMQ安装和使用(和Spring集成)
一.安装Rabbit MQ Rabbit MQ 是建立在强大的Erlang OTP平台上,因此安装Rabbit MQ的前提是安装Erlang.通过下面两个连接下载安装3.2.3 版本: 下载并安装 E ...
- 支撑Pinterest日均1000+次试验的A/B测试平台揭秘
编者按:本文详细介绍了 Pinterest 内部A/B测试平台的搭建过程,对于无论是有技术能力和资源想要自建A/B测试系统的大公司,还是想在业务中引入第三方A/B测试方法和工具的中小公司都极具参考意义 ...
- sass 基础——回顾
1.webstorm 自动编译SASS 下载安装包 http://rubyinstaller.org/downloads/ 然后点击安装,路径为默认路径就行, 勾选以下两项 add Ruby exec ...
- 读书笔记 effective c++ Item 5 了解c++默认生成并调用的函数
1 编译器会默认生成哪些函数 什么时候空类不再是一个空类?答案是用c++处理的空类.如果你自己不声明,编译器会为你声明它们自己版本的拷贝构造函数,拷贝赋值运算符和析构函数,如果你一个构造函数都没有声 ...
- Python单元测试PyUnit框架轻度整改
原理 参考:单元测试原理 背景 年后有段时间没写代码了,所以趁着周末找了个python单元测试玩下,测试自己的Android应用.发现PyUnit虽然在单个脚本文件中添加多个测试用例,比如官网提供的方 ...
- 一个web应用的诞生(6)--用户账户
之前登录注册的功能都已经完成,但是登录成功回到首页发现还是白茫茫的一片,对的,title一直都写得博客,那么最终目的也是写出一个轻博客来,但是,在发表文章之前是不是要先记录一下登录状态呢? 用户登录 ...
- hibernate 为什么持久化类时必须提供一个不带参数的默认构造函数
因为hibernate框架会调用这个默认构造方法来构造实例对象..即Class类的newInstance方法 这个方法就是通过调用默认构造方法来创建实例对象的 ,另外再提醒一点,如果你没有提供任何构造 ...