【C# in depth 第三版】温故而知新(2)
声明
本文欢迎转载,原文地址:http://www.cnblogs.com/DjlNet/p/7522869.html
前言
我们接了上一篇的未完待续,接着我们的划重点之行.....哈哈
理解:LINQ中的延迟执行的流式传输和缓冲传输
通俗的来讲就是先把数据准备好也就是取数据的逻辑已经编写好了(也就是构建好了IEumerable可迭代的数据流),但是只是准备好还没加载到内存中,然后在恰当的位置以一种“just-in-time”的方式提供(也就是在触发MoveNext的才去真的取出数据),这就是称为延迟执行。LINQ框架中总是尽量采用流式传输,在调用MoveNext的时候从迭代器中取出一个元素Current项,然后执行处理类似Where或者Cast,然后返回结果,这样一来就较少的占用了存储空间;在某些情况下又不得不采用缓冲传输,比如反转Reverse或者排序OrderBy啥的,就要求数据全部处于可用的状态也就是加载到内存中来执行批处理。类比一下就是流式传输就好像DataReader来每次处理一条记录一样,然后缓冲传输就貌似DataSet整个读取数据一样。(其中流式传输也称为惰性求值,缓冲传输也称为热情求值,它们都属于延迟执行,与其相反的是立即执行,类似返回一个单一的值Max或者ToList之类什么的,从自然的角度来看也是符合人之常情可以理解的说法),再说说Join中延迟执行,右边的数据将会被缓冲处理,而左边的数据依然会进行流式处理,所以这就是为什么尽量join对象的数据量尽量小一些的原因,那么同理在在数据库中上述的道理依然行得通。接着我们举个列子来说明延迟执行的好处,这里我们需要遍历一个Logs日志目录递归下面所有的文件的内容,找出Error对应的行内容,注意这里不会一次性加载一个日志文件所有内容,更也不会加载目下下面的所有文件内容,这里就是依赖了框架提供了流式API的调用,其实看图中的标记即可知道:
就短短的几行代码便实现了对大量日志的检索、解析过滤,这得感谢LINQ的流式处理。关于上述代码的红框部分(1)Directory.GetFiles
以及 Directory.EnumerateFiles
两个API之间的区别看名字就一目了然了吧,这里引用一下官方回答:
The EnumerateFiles and GetFiles methods differ as follows: When you use EnumerateFiles, you can start enumerating the collection of names before the whole collection is returned; when you use GetFiles, you must wait for the whole array of names to be returned before you can access the array. Therefore, when you are working with many files and directories, EnumerateFiles can be more efficient.
(2)File.ReadLines
与 File.ReadAllLines
之间的区别从返回值也可以明显的分别出来了,一个是流式加载一个立即加载,道理如同上述的第一点(1)
这样一来也同样说明了,为什么框架总是尽量尝试以一种流式的方式处理数据集,这也是为什么我们需要返回IEnumerable的原因了。
理解:Async / Await 异步编程浅析
这里呢,园子很多好文章都已经解释了怎么用呀,什么大致原理,什么状态机什么的,我这里就还是引用书中的说辞来通俗的说说,在没有C#5这么安逸的异步编程之前之后的带来的感受,举个小例子看C#团队帮我们干了什么好事儿。
不用在意界面,下面把完整代码贴出来:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
_synchronizationContext = SynchronizationContext.Current;
}
private static readonly HttpClient _httpClient = new HttpClient();
private static readonly WebClient _webClient = new WebClient();
private readonly SynchronizationContext _synchronizationContext;
private const string _url = "http://www.bing.com";
/// <summary>
/// ThreadPool方式构建异步
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
this.button1.Enabled = false;
ThreadPool.QueueUserWorkItem(x =>
{
try
{
var result = _webClient.DownloadString(_url);
_synchronizationContext.Post(length =>
{
int temp = Convert.ToInt32(length);
this.label4.Text = temp.ToString();
}, result.Length);
}
catch (Exception exception)
{
// 这里经过测试可以使用静态方法Show,原理应该也是把消息写进Winform的消息泵中,由WinForm框架自身去循环调度触发
MessageBox.Show(exception.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
// 同理下面的代码依然可以
//_synchronizationContext.Post(msg =>
//{
// var message = msg as string;
// MessageBox.Show(message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
//}, exception.Message);
}
finally
{
// 测试除UI线程之外的线程访问UI控件异常
//this.button1.Enabled = true;
_synchronizationContext.Post(empty =>
{
this.button1.Enabled = true;
}, null);
}
});
}
/// <summary>
/// Task构建异步
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
this.button2.Enabled = false;
Task.Factory.StartNew<string>(x =>
{
var result = _webClient.DownloadString(_url);
return result.Length.ToString();
}, null)
.ContinueWith(task =>
{
if (task.IsFaulted)
{
// faulted with exception
Exception ex = task.Exception;
while (ex.InnerException != null)
ex = ex.InnerException;
MessageBox.Show("Error: " + ex.Message);
}
else if (task.IsCanceled)
{
// this should not happen
// as you don't pass a CancellationToken into your task
MessageBox.Show("Canclled.");
}
else
{
// completed successfully
if (!string.IsNullOrWhiteSpace(task.Result))
{
_synchronizationContext.Post(length =>
{
int temp = Convert.ToInt32(length);
this.label3.Text = temp.ToString();
}, task.Result);
}
//MessageBox.Show("Result: " + task.Result);
}
_synchronizationContext.Post(empty =>
{
this.button2.Enabled = true;
}, null);
//// 测试除UI线程之外的线程访问UI控件异常
////this.button2.Enabled = true;
}, TaskContinuationOptions.ExecuteSynchronously);
}
/// <summary>
/// Async/Await构建异步
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button3_Click(object sender, EventArgs e)
{
this.button3.Enabled = false;
try
{
string temp = await _httpClient.GetStringAsync(_url);
this.label6.Text = temp.Length.ToString();
}
catch (Exception exception)
{
while (exception.InnerException != null)
{
exception = exception.InnerException;
}
MessageBox.Show(exception.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
this.button3.Enabled = true;
}
}
}
}
其中这里还需对上面代码 SynchronizationContext 说明一下:正是因为有了它,我们的异步async/await异步函数的后续操作,能够正确的回到UI线程执行(前提是ConfigureAwait(continueOnCaptureedContext : true) 显示的捕获调用者的上下文这里就是UI线程上下文,其中该方法默认也是参数:true ),其实这个玩意儿已经在.NET 2.0都已经有了,当时是为了提供给 BackgroundWork等组件使用,SynchronizationContext 保证了在适当的线程执行委托的概念,以至于我们调用 SynchronizationContext.Post(异步)或者 SynchronizationContext.Send (同步) 发送消息,与在 Winform中的 Control.BeginInvoke和Control.Invoke有异曲同工之妙呀!!!得注意一点就是在不同的环境模型中,同步上下文代表的含义是不一致的咯!!!这里的我们代码中的 SynchronizationContext 就代表了 UI线程的上下文信息。
接着我们通过上面的代码,看到变化点来看到好处以及C#关于异步编程怎么进化的哈,其中采用了三种不同的方式实现同一种需求,单从代码量上面或者复杂度来说都是递减的(因为这里环境是Winform所以要遵循两个原则:1、不要在UI线程上执行耗时的操作 2、不要除了UI线程之外的其他线程访问UI控件,所以代码略多了点),不过从理解上面都还是比较好理解,毕竟代码上面大家一看就应该是知道底层套路都一样,但是从体验或者感受其中包含了异常处理、线程切换自动回到正确的上下文等还是Async/Await的方式最舒服,虽然到了Task的时候有ContinueWith来衔接任务可以解决回调地狱的问题(毕竟ThreadPool可怜的还没有回调机制)。
await 的主要目的是等待耗时操作是可以避免阻塞,当方法执行到 await 表达式就返回了,当完成等待之后,后续操作依然可以回到原先的UI线程去执行,看到这里有木有一种感觉就是async/await已经帮我们把我们自己的手动实现都做好了而且做得更好做得更多,那是因为C#编译器会对所有await都构建一个后续操作,这里后续操作对于我们来就是就是this.label6.Text = temp.Length.ToString();
。关于更加详细的解读,以及内部状态机的构造和状态管理等就是比较复杂了,这里博主也不是很清楚,详情参考官方文档或者博客呗以及书中的详解篇幅也是有的,其实一般情况也不需要关心内部构造,需要关心如何去最佳实践即可。
小总结
到这里第二篇文章也差不多了,这本书的划重点也差不多了(个人来看的话,其实呢可能还有其他忽略的地方,后面CLR温故的时候再补充也是可以的),其实再看了第二遍这本书呐,给我最大感受还是对书中某些模棱两可的知识可能更加稍微掌握了些,还有就是在C#发展的里程碑中,在功能性和体验性上面来说,个人觉得还是 LINQ、Async/Await 带来的东西是给开发者最好的礼物,简直就是其他语言模仿或者学习的标杆(原谅博主活在C#的温柔乡中......),哈哈,当然了好的语言设计那肯定是要分享的嘛,不然其他开发者岂不是很难受!!而且在后面的C#6中对异步编程的await关键字做了进一步提升,具体参考微软文档。好了,重点来了,接下来博主呐,就会开始研究框架框架框架(其实也一直有关注和学习,只是感觉不能出文记录),注意是框架而不是架构哦,毕竟架构本身也是由很多框架组建起来的哦就好像基础组建与微服务的关系一样,主要是看看人家怎么设计框架的,然后才是代码是怎么写的.....。最后再说一句:**掌控自己,就是掌控敌人 --盲僧 **!!!
更新(关于优化Task构建异步 2017年9月19日00:53:55)
/// <summary>
/// Task构建异步优化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button4_Click(object sender, EventArgs e)
{
this.button4.Enabled = false;
var task1 = Task.Factory.StartNew<string>(x =>
{
var result = _webClient.DownloadString(_url);
return result.Length.ToString();
}, null);
var task2 = task1.ContinueWith(task =>
{
if (task.IsFaulted)
{
// faulted with exception
Exception ex = task.Exception;
while (ex.InnerException != null)
ex = ex.InnerException;
MessageBox.Show("Error: " + ex.Message);
}
else if (task.IsCanceled)
{
// this should not happen
// as you don't pass a CancellationToken into your task
MessageBox.Show("Canclled.");
}
else
{
// completed successfully
if (!string.IsNullOrWhiteSpace(task.Result))
{
this.label8.Text = task.Result;
}
}
this.button4.Enabled = true;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
优化说明:删除手动使用同步上下文去控制UI元素,而使用了关键的TaskScheduler.FromCurrentSynchronizationContext()
来自动使用当前的同步上下文,方法说明:创建一个与当前 System.Threading.SynchronizationContext 关联的 System.Threading.Tasks.TaskScheduler,其实折腾这玩意儿为了啥,也就是为了也能在.Net4.0的环境也就是客户端电脑还处于这个时期的时候,能够正确是姿势编写异步代码且不那么难受就好了,至于说可以使用一个nuget包Microsoft.Bcl.Async
尚未尝试过,道听途说有点小问题没亲测,不过目前来看应该还可以(瞎猜),主要是客户端的电脑人家是win7安装默认也是net4.0,但是呐他们又不想卡主界面导致未响应,其实也是数据库和网络(异地跨国调用,摊手.jpg)不给力导致的,好了该睡觉了.....晚安!老铁们....
【C# in depth 第三版】温故而知新(2)的更多相关文章
- 【C# in depth 第三版】温故而知新(1)
声明 本文欢迎转载,原文地址:http://www.cnblogs.com/DjlNet/p/7192354.html 前言 关于这本书(<深入理解C# 第三版>)的详细情况以及好坏,自行 ...
- 【C# in depth 第三版】温故而知新(1) (转)
声明 本文欢迎转载,原文地址:http://www.cnblogs.com/DjlNet/p/7192354.html 前言 关于这本书(<深入理解C# 第三版>)的详细情况以及好坏,自行 ...
- 微软发布 Windows Server 2016 预览版第三版,开发者要重点关注Nano Server
微软已经发布 Windows Server 2016 和 System Center 2016 第三个技术预览版,已经提供下载.Windows Server 2016 技术预览版第三版也是首个包括了容 ...
- 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题
调用同步锁的wait().notify().notifyAll()进行线程通信 看这个经典的存取款问题,要求两个线程存款,两个线程取款,账户里有余额的时候只能取款,没余额的时候只能存款,存取款金额相同 ...
- APUE学习--第三版apue编译
第三版apue编译: 1. 首先在 http://www.apuebook.com/ 下载源码解压: tar zxvf src.3e.tar.gz 看完Readme可知,直接执 ...
- Radmin Server-3.5 完美绿色破解版(x32 x64通用) 第三版 + 单文件制作方法
Radmin Server v3.5 汉化破解绿色版(x32 x64通用) 第三版 下载链接: https://pan.baidu.com/s/1qYVcSQo 2016年7月8日更新第三版1.修复在 ...
- 《CLR.via.C#第三版》第一部分读书笔记(一)
最近开始仔细研读<CLR.via.C#第三版>这本书.读pdf文档确实很累.建议有条件的朋友还是买书看吧. 我的笔记用来记录我对这本书的理解,简化下逻辑,对每个部分我觉得是要点的进行归纳总 ...
- 新浪微博.Net SDK第三版源代码和示例【最后一次更新了】
时间过得飞快,距离上次SDK更新已经3年有余.随着官方的不断跟新,老版SDK的部分接口已经不能正常使用.因此在QQ群里来吐槽的.来谩骂的朋友也开始多了起来.随着时代的发展,微博已经彻底的被微信甩开,因 ...
- selenium webdriver (python) 第三版
感谢 感谢购买第二版的同学,谢谢你们对本人劳动成果的支持!也正是你们时常问我还出不出第三版了,也是你们的鼓励,让我继续学习整理本文档. 感谢乙醇前辈,第二版的文档是放在他的淘宝网站上卖的,感谢他的帮忙 ...
随机推荐
- .Net Core下使用WCF
在.net core 下的wcf 和framework下的wcf使用方式有点不太一样.在core下用wc,需要安装VS扩展Visual Studio WCF Connected Service,目前这 ...
- 在Eclipse中关联Android API源码
在Eclipse中快速关联API源码,便于查看类以及方法.方法如下: 1. 在对应的项目文件右键——>properties——>java build path——>libraries ...
- Linux-进程描述(2)之进程标识符进程位置与环境变量
在上一篇文章中详细介绍了task_struct结构体内的常见成员,然后我们就来看一下具体内容.每个进程都把它的信息放在 task_struct 这个数据结构中,task_struct 包含了这些内容: ...
- JMeter安装和简介
1.Apache jmeter 是一个100%的纯java桌面应用,用于压力测试和性能测量.它最初被设计用于Web应用测试但后来扩展到其他测试领域,可以用于对静态的和动态的资源(文件,Servlet, ...
- Intel VT-X
VT-x是intel运用Virtualization虚拟化技术中的一个指令集,是CPU的硬件虚拟化技术,VT可以同时提升虚拟化效率和虚拟机的安全性,在x86平台上的VT技术,一般称之为VT-x,而在I ...
- 【Ubuntu 16】安装net-snmp
使用tar.gz压缩包安装mongodb时报错,没有库文件 libnetsnmpmibs.so.3o cannot open file or directory 在网上找了一篇文章 需要安装net-s ...
- python的while嵌套 99乘法表 三角形和正方形
python的99乘法表 num1=1 while num1<=9 : num2 = 1 while num2 <=num1 : print (str(num2)+"X" ...
- Centos7.2下Nginx配置SSL支持https访问(站点是基于.Net Core2.0开发的WebApi)
准备工作 1.基于nginx部署好的站点(本文站点是基于.Net Core2.0开发的WebApi,有兴趣的同学可以跳http://www.cnblogs.com/GreedyL/p/7422796. ...
- JQuery操作iframe父页面与子页面的元素与方法
JQuery操作iframe父页面与子页面的元素与方法 JQUERY IFRAME 下面简单使用Jquery来操作iframe的一些记录,这个使用纯JS也可以实现. 第一.在iframe中查找父页面元 ...
- MongoDB导入导出以及数据库备份
-------------------MongoDB数据导入与导出------------------- 1.导出工具:mongoexport 1.概念: mongoDB中的m ...