标签归档转发区【C#】

澳门新葡亰官网【转】异步编程 In .NET

概述

  在前写的平等篇有关async和await的前生今生的篇章以后,大家像以async和await提高网站拍卖能力方面还有局部疑点,博客园本身吗召开了成千上万的尝尝。今天我们重来答复瞬间以此问题,同时我们见面举行一个async和await在WinForm中之尝试,并且相比在4.5前的异步编程模式APM/EAP和async/await的分,最后咱们尚见面追在不同线程之间互相的题目。

  IIS存在着拍卖能力的题材,但是WinForm却是UI响应的题目,并且WinForm的UI线程至始至终都是跟一个,所以两者之间有必然的区分。有人会咨询,现在尚有人形容WinForm吗?好吧,它实在是一个比较老的事物吧,不如WPF炫,技术为不如WPF先进,但是自从架构层面来讲,不管是Web,还是WinForm,又或许WPF,Mobile,这些都只是表现层,不是啊?现在的特大型系统一般桌面客户端,Web端,手机,平板端都见面提到,这也是为什么会产生应用层,服务层的存在。我们当马上谈论的ASP.NET
MVC,WinForm,WFP,Android/IOS/WP
都是展现层,在表现层我们应只是处理以及“表现”相关的逻辑,任何和业务有关的逻辑应该都是位于下层处理的。关于架构的题目,我们后面又逐级深入,另外别说我没提示而,我们今天尚会见看到.NET中另外一个曾经老去的技巧Web
Service。

  还得唤醒您,文章内容有硌长,涉及的知识点比较多,所以,我推荐:”先到后看“
,先到后关禁闭是21世纪看长篇的首选的志,是上佳关系的开头,想了解凡是什么会让您非常吧?想知道怎么上海今会见下蛋如此老之雨么?请牢记先到后关禁闭,你及的非是本身之稿子,而是我们冒着大雨还要去上班的可贵精神!先到后关禁闭,你值得拥有!

目录

  • async/await如何升级IIS响应能力
    • 并行处理的步骤
    • 哪些因素影响了咱的应能力
    • async 和
      await做了呀?
    • 差一点接触建议
  • 头Web
    Service的异步模式APM

    • 当WinForm遇到Web
      Service
    • WinForm异步调用Web
      Service
  • APM异步编程详解
    • 线程问题
    • 从Delegate开始
    • 再也认识APM
  • EAP(Event-Based Asynchroronous
    Pattern)

    • 线程问题
  • async/await
    给WinForm编程带来了啊?
  • 差线程之间通讯的问题
    • 万能的InvokeI
    • SynchronizationContex上下文同步对象
  • 小结
  • 引用 &
    扩展阅读

async/await如何升级IIS处理能力

  首先应能力连无全是说咱先后性能的问题,有时候可能你的程序尚未另外问题,而且仔细经过优化,可是响应能力或没上来,网站性能分析是一个繁杂的活着,有时候只能依赖经验与缕缕的尝尝才能够达比较好的效果。当然我们今天谈论的重中之重是IIS的拍卖能力,或者也可能就是IIS的习性,但绝非代码本身的属性。即使async/await能够增进IIS的拍卖能力,但是对用户来说一切页面从发起呼吁到页面渲染完成的这些日,是勿会见因咱们加以了async/await之后发生多异常变之。

  另外异步的ASP.NET并非只有async/await才堪开的,ASP.NET在Web
Form时代就已经发异步Page了,包括ASP.NET
MVC不是吧时有发生异步的Controller么?async/await
很新,很非常,但是它为只是于本来一技基础及开了一部分改良,让程序员们写于异步代码来重新易于了。大家经常说微软喜爱新瓶装旧酒,至少我们要察看此新瓶被我们带来了什么,不管是其他产品,都非可能同样开始即够呛圆满,所以持续的迭代创新,也得以说凡是同等种科学做事的方式。

ASP.NET并行处理的步调

   ASP.NET是哪些当IIS中行事之相同轻柔就老详细的牵线了一个请是哪由客户端到服务器的HTTP.SYS最后进入CLR进行处理的(强烈建议不打听就无异块的同班先押就首文章,有助于你懂本小节),但是拥有的步子都是基于一个线程的若下展开的。IIS本身就是是一个差不多线程的劳作条件,如果我们由多线程的观点来看会发生什么变化吧?我们先是来拘禁一下底就张图。注意:我们下面的步调是建立于IIS7.0以后的融会模式基础之上的。(注:下面这张图在dudu的提醒后,重新开了有的找工作,做了有改成,w3dt这同一步来自于博客园社对题目的不断探索,详情可触发这里)

澳门新葡亰官网 1

  我们再次来梳理一下端的手续:

  1. 所有的呼吁最初步是出于HTTP.SYS接收的,HTTP.SYS内部发生一个队列维护着这些请求,这个队的request的多寡超越一定数额(默认是1000)的上,HTTP.SYS就会见直接返回503状态(服务器忙),这是咱们的首先独阀门。性能计数指标:“Http
    Service Request Queues\CurrentQueueSize
  2. 出于w3dt负责把要从HTTP.SYS
    的队列中置一个遥相呼应端口的序列中,据非官方资料显示该队长度也能啊20(该队是非公开的,没有文档,所以也尚未性计数器)。
  3. IIS
    的IO线程从达平等步的队列中收获请求,如果是急需ASP.NET处理的,就会传递给CLR
    线程池的Worker 线程,IIS的IO线程继续回来重新做该步骤。CLR
    线程池的Worker线程数量是亚个阀门。
  4. 当CLR中正为拍卖的请求数据超出一定值(最特别并行处理请求数量,.NET4过后默认是5000)的时光,从IO线程过来的伸手虽不见面一直提交Worker线程,而是坐一个进程池级别的一个班了,等到这数据仅次于临界值的时段,才见面把它再提交Worker线程去处理。这是咱的老三独阀门。
  5. 达等同步着说到的要命进程池级别之序列有一个长短的限制,可以透过web.config里面的processModel/requestQueueLimit来设置。这可说吧是一个阀门。当在处理的数码过所许的尽深并行处理请求数量的时刻,我们就会见得到503了。可以由此性能计数指标:“ASP.NET
    v4.0.30319\Requests Queued
    ” 来查看该队的长。

 哪些因素会决定我们的响应能力

  从上面我们涉了几很阀门中,我们得汲取下面的几单数字控制或者说影响在我们的应能力。

  1. HTTP.SYS队列的尺寸
  2. CLR线程池最深Worker线程数量
  3. 尽酷并行处理请求数量
  4. 进程池级别队列所许的长

HTTP.SYS队列的尺寸

  斯自家当无欲格外解释,默认值是1000。这个价在我们我们后IIS
IO线程和Worker线程的处理速度,如果她简单只都处理不了,这个数字再度好也并未因此。因为最后他们见面被贮存到进程池级别之行中,所以只是会招内存的浪费。

不过充分Worker线程数量

  这个价值是可于web.config中进行配置的。

澳门新葡亰官网 2

  maxWorkerThreads: CLR中真正处理要的极端深Worker线程数量
  minWorkerThreads:CLR中诚处理要的卓绝小Worker线程数量

  minWorkerThreads的默认值是1,合理的加大他们得避不必要之线程创建同销毁工作。

极端深并行处理请求数量

  进程池级别之班给咱们的CLR一定的缓冲,这中如留意的是,这个班还未曾进来到CLR,所以其不会见占用我们托管环境的别样资源,也就是是将请求卡在了CLR的外面。我们用在aspnet.config级别进行部署,我们得以.net
fraemwork的设置目录下找到它们。一般是 C:\Windows\Microsoft.NET\Framework\v4.0.30319
如果你安装的凡4.0之语句。

澳门新葡亰官网 3

  maxConcurrentRequestPerCPU:
每个CPU所兴的顶老并行处理请求数量,当CLR中worker线程正在处理的呼吁的与超过此累时,从IO线程过来的求虽会于放到我们进程池级别的队列中。
  maxConcurrentThreadsPerCPU: 设置为0即禁用。
  requestQueue: 进程池级别队列所兴的长度  

async和await 做了啊?

  我们总算使切入主题了,拿ASP.NET
MVC举例,如果不下async的Action,那么得,它是当一个Woker线程中实践的。当我们走访一些web
service,或者读文件的时刻,这个Worker线程就见面于封堵。假设我们这Action执行时间一起是100ms,其它访问web
service花了80ms,理想图景下一个Worker线程一秒可以响应10单请求,假而我们的maxWorkerThreads是10,那我们一样秒内连续可响应请求虽是100。如果说我们怀念把此可响应请求数升到200庸开吗?

  有人会说,这尚免略,把maxWorkerThreads调20免就执行了呢?
其实我们做啊从不什么
问题,确实是得的,而且也确实会于及意向。那我们为什么还要大费周章的动手什么
async/await呢?搞得脑子都晕了?async/await给咱们缓解了啊问题?它可以当我们走访web
service的下把当下底worker线程放走,将它放回线程池,这样她就足以错过处理外的恳求了。等及web
service给咱们回到结果了,会重到线程池中随心所欲以一个新的woker线程继续朝着生实施。也就是说我们减了那么有等候的时光,充份利用了线程。

    我们来比一下以async/awit和未采取的景况,

  不应用async/await: 20个woker线程1s得拍卖200单请求。

  那转换成总的时空的哪怕是 20 * 1000ms =  20000ms,
  其中等待的时间啊 200 * 80ms = 16000ms。
  也就是说使用async/await我们足足节约了16000ms的年华,这20独worker线程又见面再夺处理要,即使如约每个请求100ms的拍卖时我们尚可以重复充实160只请求。而且转移忘了100ms是基于联合情况下,包括等待时在内的根基及得的,所以实际上状况恐怕还要多,当然我们这里没算上线程切换的时刻,所以实际情形被是有好几差别的,但是当无会见非常死,因为我们的线程都是冲线程池的操作。
  所有结果是20只Worker线程不应用异步的状况下,1s能自理200独请求,而动异步的景况下得以处理360个请求,立马提升80%呀!采用异步之后,对于同的求数量,需要的Worker线程数据会大大减少50%横,一个线程至少会以积上分红1M的内存,如果是1000单线程那便是1G之容量,虽然内存现在方便,但是看着到底是好之嘛,而且更不见的线程是得抽线程池以保障线程时发的CPU消耗的。另:dudu分享 CLR1秒之内只能创造2个线程。

  注意:以上数量并非真实测试数据,真实情况一个request的时刻呢休想100ms,花费在web
service上之工夫啊不用80ms,仅仅是叫大家一个思路:),所以这中间所以了async和await之后对响应能力有差不多不胜的升级换代和咱们本堵塞在这些IO和网及的岁月是出死要命之涉嫌之。

几乎点建议

  看此,不了解大家产生没发获取点什么。首先第一沾我们只要清楚之是async/await不是万能药,不们不能够想光写点儿个光键字就算指望性能的升迁。要切记,一个CPU在同一时间段内是不得不执行一个线程的。所以马上为是怎async和await建议于IO或者是网络操作的时段使用。我们的MVC站点访问WCF或者Web
Service这种现象就是挺之契合采取异步来操作。在地方的例证中80ms读取web
service的工夫,大部份时间还是勿欲cpu操作的,这样cpu才方可于外的线程利用,如果不是一个读取web
service的操作,而是一个繁杂计算的操作,那尔就是当着cpu爆表吧。

  第二沾是,除了次中利用异步,我们地方讲到之有关IIS的部署是十分重要之,如果下了异步,请记得将maxWorkerThreads和maxConcurrentRequestPerCPU的值调高试试。

 早期对Web service的异步编程模式APM

  说得了我们伟大上的async/await之后,我们来看看是技能非常老,但是概念确依旧持续至今天之Web
Service。 我们这边所说的对web
service的异步编程模式不是恃以劳动器端的web service本身,而是借助调用web
service的客户端。大家知晓对web service,我们经过增补加web
service引用或者.net提供的成形工具就得生成对应的代理类,可以为咱们像调用本地代码一样看web
service,而所大成的代码类吃对针对性各国一个web
service方法生成3单照应的艺术,比如说我们的不二法门名叫DownloadContent,除了这艺术外还有BeginDownloadContent和EndDownloadContent方法,而立半个就是咱今天如说的头的异步编程模式APM(Asynchronous
Programming Model)。下面就是来探望我们web
service中的代码,注意我们今天之花色都是在.NET Framework3.5下实现之。

 PageContent.asmx的代码

public class PageContent : System.Web.Services.WebService
{
    [WebMethod]
    public string DownloadContent(string url)
    {
        var client = new System.Net.WebClient();
        return client.DownloadString(url);
    }
}

  注意我们web
service中的DownloadContent方法调用的是WebClient的齐方法,WebClient也产生异步方法就是:DownloadStringAsync。但是大家只要知道,不管服务器是一块还是异步,对于客户端的话调用了公是web
service都是一样的,就是得相当而回去结果。

  当然,我们呢可像MVC里面的代码一样,把咱的服务器端也写成异步的。那得好处的是那个托管web
service的服务器,它的拍卖能力赢得提高,就像ASP.NET一样。如果我们用JavaScript去调用这个Web
Service,那么Ajax(Asynchronous Javascript +
XML)就是咱们客户端用到的异步编程技术。如果是外的客户端也?比如说一个CS的桌面程序?我们得异步编程么?

当WinForm遇上Web Service

  WinForm不像托管在IIS的ASP.NET网站,会时有发生一个线程池管理在多只线程来处理用户的恳求,换个说法ASP.NET网站生来就冲多线程的。但是,在WinForm中,如果我们不刻意用多线程,那至始至终,都单来一个线程,称之为UI线程。也许在一些小型的网中WinForm很少涉及到多线程,因为WinForm本身的优势就于她是单独运转在客户端的,在性能上同操作性上且见面发生很非常的优势。所以广大中小型的WinForm系统还是一直就是访数据库了,并且多也仅生数据的传导,什么图片资源那是老少之,所以待的时空是生紧缺的,基本不用花啊脑子去考虑什么3秒内要将页面显示到用户面前这种题材。

  既然WinForm在性能上发出这样可怜的优势,那她还需异步吗?

  我们地方说之是中小型的WinForm,如果是重型的体系也?如果WinForm只是其他的雅有些片段,就像咱文章开始说的还有许多别众多个手机客户端,Web客户端,平板客户端也?如果客户端很多导致数据库撑不停歇怎么惩罚?
想当当中加相同交汇缓存怎么收拾?

  以一个b2b的网站功能举例,用户可以由此网站下单,手机吗得下单,还可以通过电脑的桌面客户端下单。在生完单之后要到位市,库存扣减,发送订单确认通知等等功能,而无论是您的订单是通过哪个端好的,这些功能我们都如去开,对啊?那咱们就算非可知独在WinForm里面了,不然这些代码在另外的端里又得全体新再相继实现,同样的代码放在不同的地方那不过相当危险的,所以就算时有发生了我们后来的SOA架构,把这些成效都缩减成服务,每种型的捧都是调用服务就好了。一凡是可统一保护这些作用,二是可以充分有益于之开扩展,去还好的服功能跟架构上之扩张。比如说像下这样的一个系。

 澳门新葡亰官网 4

  在达标图中,Web端虽然为是属我们平常说之服务端(甚至是由多华服务器组成的web群集),但是针对我们一切系统吧,它为特是一个端而已。对于一个端的话,它自己就处理和用户交互的题目,其余具备的效应,业务还见面付出后来台处理。在我们地方的架中,应用层都非会见一直到位真正业务逻辑相关的拍卖,而是坐我们重新下层数据层去开拍卖。那么应用层主要协助做有同用户交互的一对效,如果手机短信发送,邮件发送等等,并且可因先级选择是放开入行中稍候处理或一直调用功能服务就处理。

  以如此的一个体系受到,我们的Web服务器可以,Winform端也好都将仅是总体系统被的一个巅峰,它们主要的另外是用户与后服务中的一个大桥。涉及到Service的调用之后,为了让用户良好的用户体验,在WinForm端,我们本将考虑异步的问题。 

WinForm异步调用Web Service

  有矣诸如VS这样强劲的工具为我们转变代理类,我们以形容调用Web
service的代码时即足以像调用本地类库一样调用Web
Service了,我们无非待加上一个Web Reference就可了。

澳门新葡亰官网 5

// Form1.cs的代码

private void button1_Click(object sender, EventArgs e)
{
    var pageContentService = new localhost.PageContent();
    pageContentService.BeginDownloadContent(
        "http://jesse2013.cnblogs.com",
        new AsyncCallback(DownloadContentCallback),
        pageContentService);
}

private void DownloadContentCallback(IAsyncResult result)
{
    var pageContentService = (localhost.PageContent)result.AsyncState;
    var msg = pageContentService.EndDownloadContent(result);
    MessageBox.Show(msg);
}

  代码非常之简易,在实行完pageContentService.BeginDownloadContent之后,我们的主线程就赶回了。在调用Web
service这段时日内我们的UI不会被打断,也未会见起“无法响应这种情景”,我们还可拖动窗体甚至举行其他的业务。这便是APM的魔力,但是咱的callback究竟是以谁线程中实践的也?是线程池中之丝程么?咋们接着朝下看。

APM异步编程模式详解

线程问题

  接下去我们便是重复进一步的问询APM这种模式是如何做事的,但是首先我们要对上面留下来的题材,这种异步的编程方式发出没有产生呢我们被新的线程?让代码说话:

private void button1_Click(object sender, EventArgs e)
{
    Trace.TraceInformation("Is current thread from thread pool? {0}", Thread.CurrentThread.IsThreadPoolThread ? "Yes" : "No");
    Trace.TraceInformation("Start calling web service on thread: {0}", Thread.CurrentThread.ManagedThreadId);
    var pageContentService = new localhost.PageContent();
    pageContentService.BeginDownloadContent(
        "http://jesse2013.cnblogs.com",
        new AsyncCallback(DownloadContentCallback),
        pageContentService);
}

private void DownloadContentCallback(IAsyncResult result)
{
    var pageContentService = (localhost.PageContent)result.AsyncState;
    var msg = pageContentService.EndDownloadContent(result);

    Trace.TraceInformation("Is current thread from thread pool? {0}" , Thread.CurrentThread.IsThreadPoolThread ? "Yes" : "No");
    Trace.TraceInformation("End calling web service on thread: {0}, the result of the web service is: {1}",
        Thread.CurrentThread.ManagedThreadId,
        msg);
}

  我们于按钮点击的措施及callback方法中分别出口当前线程的ID,以及她们是不是属于线程池的线程,得到的结果如下:

  Desktop4.0.vshost.exe
Information: 0 : Is current thread a background thread? NO
  Desktop4.0.vshost.exe
Information: 0 : Is current thread from thread pool? NO
  Desktop4.0.vshost.exe
Information: 0 : Start calling web service on thread: 9
  Desktop4.0.vshost.exe
Information: 0 : Is current thread a background thread? YES
  Desktop4.0.vshost.exe
Information: 0 : Is current thread from thread pool? YES
  Desktop4.0.vshost.exe
Information: 0 : End calling web service on thread: 14, the result of
the web service is: <!DOCTYPE html>…

  按钮点击的法门是出于UI直接决定,很显眼它不是一个线程池线程,也非是后台线程。而我们的callback却是以一个来自于线程池的后台线程执行的,答案揭晓了,可是这会于咱们带一个题材,我们地方说了才出UI线程也得以错过创新我们的UI控件,也就是说在callback中我们是勿可知更新UI控件的,那我们如何给更新UI让用户了解反馈也?答案于后头接晓
:),让咱先行注意让把APM弄明白。

从Delegate开始

  其实,APM在.NET3.5先都给广大采用,在WinForm窗体控制着,在一个IO操作的类库中等等!大家好十分易之找到搭配了Begin和End的点子,更要紧的凡设是有代理的地方,我们都得以用APM这种模式。我们来拘禁一个好简短的例子:

delegate void EatAsync(string food);
private void button2_Click(object sender, EventArgs e)
{
    var myAsync = new EatAsync(eat);
    Trace.TraceInformation("Activate eating on thread: {0}", Thread.CurrentThread.ManagedThreadId);
    myAsync.BeginInvoke("icecream", new AsyncCallback(clean), myAsync);
}

private void eat(string food)
{
    Trace.TraceInformation("I am eating.... on thread: {0}", Thread.CurrentThread.ManagedThreadId);
}

private void clean(IAsyncResult asyncResult)
{
    Trace.TraceInformation("I am done eating.... on thread: {0}", Thread.CurrentThread.ManagedThreadId);
}

  上面的代码中,我们透过把eat封装成一个委托,然后再度调用该信托的BeginInvoke方法实现了异步的履。也就算是实际上的eat方法不是以主线程中推行之,我们可以拘留输出的结果:

  Desktop4.0.vshost.exe
Information: 0 : Activate eating on thread: 10
  Desktop4.0.vshost.exe
Information: 0 : I am eating…. on thread: 6
  Desktop4.0.vshost.exe
Information: 0 : I am done eating…. on thread: 6

  clean是咱污染进的callback,该方法会在我们的eat方法执行了事后为调用,所以它见面及我们eat方法以同一个线程中于调用。大家而熟悉代理的口舌就会见理解,代码实际上会被编译成一个近似,而BeginInvoke和EndInvoke方法正是编译器为咱机关加进去的主意,我们无用额外做任何事情,这在首没有TPL和async/await之前(APM从.NET1.0时就是产生矣),的确是一个是的精选。

更认识APM

打探了Delegate实现的BeginInvoke和EndInvoke之后,我们重来分析一下APM用到的那些对象。
拿我们Web service的代办类来举例,它吗咱转变了以下3独道:

  1. string DownloadContent(string url): 同步方法
  2. IAsyncResult BeginDownloadContent(string url, AsyncCallback
    callback, object asyncState): 异步开始计
  3. EndDownloadContent(IAsyncResult asyncResult):异步结束方法

  于我们调用EndDownloadContent方法的早晚,如果我们的web

service调用还尚未回来,那这个时就是见面为此阻塞的不二法门去用结果。但是以咱们传到BeginDownloadContent中之callback被调用的时候,那操作必然是早已到位了,也就是说IAsyncResult.IsCompleted

true。而在APM异步编程模式中Begin方法总是回到IAsyncResult这个接口的兑现。IAsyncReuslt仅仅包含以下4独特性:

澳门新葡亰官网 6

  WaitHanlde通常作为同对象的基类,并且可使它来阻塞线程,更多信息方可参考MSDN.aspx) 。
借助于IAsyncResult的辅,我们就算可由此以下几栽艺术去赢得当前所执操作的结果。

  1. 轮询
  2. 强制等
  3. 得通知

  完成通知就是等以”WinForm异步调用WebService”那收着之所以到的道,调了Begin方法后,主线程就算就任务了。我们吧未用监督该操作的尽情况,当该操作实践了之后,我们当Begin方法吃传上的callback就会吃调用了,我们得于深方式吃调用End方法去取得结果。下面我们重新略说一下面前两种植方法。

//轮询获取结果代码

var pageContentService = new localhost.PageContent();
IAsyncResult asyncResult = pageContentService.BeginDownloadContent(
    "http://jesse2013.cnblogs.com",
    null,
    pageContentService);

while (!asyncResult.IsCompleted)
{
    Thread.Sleep(100);
}
var content = pageContentService.EndDownloadContent(asyncResult);

 // 强制等结果代码

var pageContentService = new localhost.PageContent();
IAsyncResult asyncResult = pageContentService.BeginDownloadContent(
    "http://jesse2013.cnblogs.com",
    null,
    pageContentService);

// 也可以调用WaitOne()的无参版本,不限制强制等待时间
if (asyncResult.AsyncWaitHandle.WaitOne(2000))
{
    var content = pageContentService.EndDownloadContent(asyncResult);
}
else
{ 
    // 2s时间已经过了,但是还没有执行完   
}

EAP(Event-Based Asynchronous Pattern)

  EAP是于.NET2.0出产的外一样种过渡的异步编程模型,也是以.NET3.5从此Microsoft支持的相同种植做法,为什么呢?
如果大家打一个.NET4.0或者又强版本的WinForm项目,再失加加Web
Reference就见面发现变化的代办类吃就远非Begin和End方法了,记住在3.5的当儿是双方共存之,你得挑选随机一栽来用。但是到了.NET4.0事后,EAP成为了卿唯一的选取。(我从不尝试了手动生成代理类,有趣味的同校可以品尝一下)让咱来拘禁一下当.NET4产,我们是怎异步调用Web
Service的。

private void button1_Click(object sender, EventArgs e)
{
    var pageContent = new localhost.PageContent();
    pageContent.DownloadContentAsync("http://jesse2013.cnblogs.com");
    pageContent.DownloadContentCompleted += pageContent_DownloadContentCompleted;
}

private void pageContent_DownloadContentCompleted(object sender, localhost.DownloadContentCompletedEventArgs e)
{
    if (e.Error == null)
    {
        textBox1.Text = e.Result;
    }
    else
    { 
        // 出错了
    }
}

线程问题

  不知底大家还是否记得,在APM模式被,callback是实施于另一个线程中,不可知随易的错过更新UI。但是只要您仔细看一下者的代码,我们的DownloadContentCompleted事件绑定的办法中一直就创新了UI,把返回的情写及了一个文本框里面。通过同样的法门可发现,在EAP这种异步编程模式下,事件绑定的方吧是以调用的充分线程中尽之。也就是说解决了异步编程的时光UI交互的题目,而且是于与一个线程中履行。
看看下面的代码:

private void button1_Click(object sender, EventArgs e)
{
    Trace.TraceInformation("Call DownloadContentAsync on thread: {0}", Thread.CurrentThread.ManagedThreadId);
    Trace.TraceInformation("Is current from thread pool? : {0}", Thread.CurrentThread.IsThreadPoolThread ? "YES" : "NO");

    var pageContent = new localhost.PageContent();
    pageContent.DownloadContentAsync("http://jesse2013.cnblogs.com");
    pageContent.DownloadContentCompleted += pageContent_DownloadContentCompleted;
}

private void pageContent_DownloadContentCompleted(object sender, localhost.DownloadContentCompletedEventArgs e)
{
    Trace.TraceInformation("Completed DownloadContentAsync on thread: {0}", Thread.CurrentThread.ManagedThreadId);
    Trace.TraceInformation("Is current from thread pool? : {0}", Thread.CurrentThread.IsThreadPoolThread ? "YES" : "NO");
}

  Desktop4.vshost.exe
Information: 0 : Call DownloadContentAsync on thread: 10

  Desktop4.vshost.exe
Information: 0 : Is current from thread pool? : NO

  Desktop4.vshost.exe
Information: 0 : Completed DownloadContentAsync on thread: 10

  Desktop4.vshost.exe
Information: 0 : Is current from thread pool? : NO

async/await 给WinFrom带来了什么

  如果说async给ASP.NET带来的是处理能力的增高,那么当WinForm中让程序员带来的利益虽是极端深之。我们还为毫不因为要是贯彻异步写回调或者绑定事件了,省事了,可读性也增长了。不信仰你看下我们以调用我们那个web
service的代码在.NET4.5产实现转:

private async void button2_Click(object sender, EventArgs e)
{
    var pageContent = new localhost.PageContentSoapClient();
    var content = await pageContent.DownloadContentAsync("http://jesse2013.cnblogs.com");

    textBox1.Text = content.Body.DownloadContentResult;
}

  简单的老三实践代码,像写同步代码一样写异步代码,我思可能就就算是async/await的魔力吧。在await之后,UI线程就可回到响应UI了,在地方的代码中我们是没有初线程产生的,和EAP一样以到结果一直就好本着UI操作了。

  async/await似乎真正很好,但是一旦我们await后面的代码执行在另外一个线程中会发什么业务为?

private async void button1_Click(object sender, EventArgs e)
{
    label1.Text = "Calculating Sqrt of 5000000";
    button1.Enabled = false;
    progressBar1.Visible = true;

    double sqrt = await Task<double>.Run(() =>
    {
        double result = 0;
        for (int i = 0; i < 50000000; i++)
        {
            result += Math.Sqrt(i);

            progressBar1.Maximum = 50000000;
            progressBar1.Value = i;
        }
        return result;
    });

    progressBar1.Visible = false;
    button1.Enabled = true;
    label1.Text = "The sqrt of 50000000 is " + sqrt;
}

  我们于界面中推广了一个ProgressBar,同时起一个线程去管由1及5000000之平方全部加以起来,看起是一个雅耗时的操作,于是我们为此Task.Run开了一个初的线程去实践。(注:如果是纯粹运算的操作,多线程操作对性能没多老帮扶,我们这里关键是怀念给UI一个快显示当前展开到啦一样步了。)看起没呀问题,我们本F5运行吧!
  Bomb~

澳门新葡亰官网 7

  当尽及这边的时,程序即使夭折了,告诉我们”无效操作,只能从创立porgressBar的线程访问它。“
 这也是咱们一样开始波及的,在WinForm主次中,只有UI主线程才能够针对UI进行操作,其它的线程是无权限的。接下来我们便来探,如果以WinForm中落实非UI线程对UI控制的更新操作。 

不等线程之间通讯的问题

万能的Invoke

  WinForm中多数的控件包括窗体在内都实现了Invoke.aspx)方法,可以流传一个Delegate,这个Delegate将会晤受有十分控制的线程所调用,从而避免了跨线程访问的题目。

Trace.TraceInformation("UI Thread : {0}", Thread.CurrentThread.ManagedThreadId);
double sqrt = await Task<double>.Run(() =>
{
    Trace.TraceInformation("Run calculation on thread: {0}", Thread.CurrentThread.ManagedThreadId);
    double result = 0;
    for (int i = 0; i < 50000000; i++)
    {
        result += Math.Sqrt(i);
        progressBar1.Invoke(new Action(() => {
            Trace.TraceInformation("Update UI on thread: {0}", Thread.CurrentThread.ManagedThreadId);
            progressBar1.Maximum = 50000000;
            progressBar1.Value = i;
        }));
    }
    return result;
});

  Desktop.vshost.exe Information: 0 : UI
Thread : 9
  Desktop.vshost.exe Information: 0 : Run calculation on
thread: 10
  Desktop.vshost.exe
Information: 0 : Update UI on thread: 9

  Invoke方法比较简单,我们虽未开过多的钻研了,但是我们如果考虑到一点,Invoke是WinForm实现之UI跨线程沟通方式,WPF用底可是Dispatcher,如果是于ASP.NET下跨线程之间的联合又怎处置为。为了配合各种技能平台下,跨线程同步的题材,Microsoft在.NET2.0的时节就是引入了咱下面的此目标。

SynchronizationContext上下文同步对象

怎么要SynchronizationContext

  就像我们在WinForm中碰到的题材同,有时候我们用在一个线程中传递一些数码要做一些操作及另外一个线程。但是于大部分状下这是不允的,出于安全因素的设想,每一个线程都有她独立的内存空间和上下文。因此在.NET2.0,微软推出了SynchronizationContext。

  它要的意义有是吧我们提供了同一种植将片行事职责(Delegate)以行的不二法门囤于一个上下文对象中,然后把这些上下文对象关联到现实的线程上,当然有时候多单线程也足以提到到同一个SynchronizationContext对象。获取当前线程的一路上下文对象足以下SynchronizationContext.Current。同时其还也咱提供以下简单单道Post和Send,分别是以异步和联合的法以我们地方说之办事任务放到我们SynchronizationContext的序列中。

SynchronizationContext示例

  还是拿我们地方Invoke中因故到之例子举例,只是这次我们不直接调用控件的Invoke方法去创新她,而是写了一个Report的法子专门去更新UI。

double sqrt = await Task<double>.Run(() =>
{
    Trace.TraceInformation("Current thread id is:{0}", Thread.CurrentThread.ManagedThreadId);

    double result = 0;
    for (int i = 0; i < 50000000; i++)
    {
        result += Math.Sqrt(i);
        Report(new Tuple<int, int>(50000000, i));
    }
    return result;
});

  每一样差操作了后我们调用一下Report方法,把咱一起要算的数字,以及当前着测算的数字传为她便好了。接下来就是扣留我们的Report方法了。

private SynchronizationContext m_SynchronizationContext;
private DateTime m_PreviousTime = DateTime.Now;

public Form1()
{
    InitializeComponent();
    // 在全局保存当前UI线程的SynchronizationContext对象
    m_SynchronizationContext = SynchronizationContext.Current;
}

public void Report(Tuple<int, int> value)
{
    DateTime now = DateTime.Now;
    if ((now - m_PreviousTime).Milliseconds > 100)
    {
        m_SynchronizationContext.Post((obj) =>
        {
            Tuple<int, int> minMax = (Tuple<int, int>)obj;
            progressBar1.Maximum = minMax.Item1;
            progressBar1.Value = minMax.Item2;
        }, value);

        m_PreviousTime = now;
    }
}

  整个操作看起要较Inovke复杂一点,与Invoke不同的是SynchronizationContext不待对Control的援,而Invoke必须先得生深控件才会调用它的Invoke方法对其进行操作。

小结

  这篇博客内容发生硌长,不晓出微人足见见这里:)。最开头我只是怀念写写WinFrom下异步调用Web
Service的组成部分事物,在相同初步马上首文书之题目是”异步编程在WinForm下的施行“,但是写着形容着发现越来越多的迷团没有解开,其实都是片尽的技巧以前没点和掌握好,所以所幸就一次性将他们还重新学习了平通,与大家享用。

  我们重来回顾一下篇章所涉嫌到的有些着重的定义:

  1. async/await
    于ASP.NET做的顶老奉献(早期ASP.NET的异步开发模式同样为来这么的孝敬),是以走访数据库的时刻、访问远程IO的早晚马上放出了目前之拍卖性程,可以让这些线程回到线程池中,从而实现好去处理其他要的功用。
  2. 异步的ASP.NET开发能当拍卖能力达到带多特别之提高,取决于我们的顺序来多少时间是叫封堵的,也就是那些访问数据库和长距离Service的时间。
  3. 除开将代码改化异步,我们还得在IIS上举行片对立的配置来实现最优化。
  4. 任是ASP.NET、WinForm还是Mobile、还是平板,在巨型系统遭到都只是是一个和用户交互的端而已,所以不管您本凡召开所谓的前端(JavaScript +
    CSS等),还是所谓的后端(ASP.NET MVC、WCF、Web API 等
    ),又或是比时的移动端(IOS也好,Andrioid也罢,哪怕是免争气的WP),都只是整套大型系统被的琐碎一角而已。当然我并无是降级这些端的价,正是以咱们注意让不同,努力提高每一个端的用户体验,才能够被这些巨型系统有露脸的时机。我想说之是,在你针对今天技术得到一定之得后,不要停止学习,因为任何软件架构体系受到还有多众多美之事物值得我们去发现。
  5. APM和EAP是在async/await之前的有限种不同之异步编程模式。
  6. APM如果不打断主线程,那么成功通知(回调)就会尽于另外一个线程中,从而为我们更新UI带来一定之题目。
  7. EAP的通告事件是于主线程中执的,不见面设有UI交互的问题。
  8. 最终,我们还读了于Winform下殊线程之间相的问题,以及SynchronizationContext。
  9. APM是.NET下最早的异步编程方法,从.NET1.0来说就发了。在.NET2.0底上,微软意识及了APM的回调函数中与UI交互的题目,于是带来了新的EAP。APM与EAP一直并存到.NET3.5,在.NET4.0之时刻微软带来了TPL,也即是咱们所熟识的Task编程,而.NET4.5不怕是咱大家理解之async/await了,可以看到.NET一直在非停歇的上进,加上多年来频频的及开源社区的搭档,跨平台等风味的引入,我们发出理由相信.NET会更加活动越好。

  最后,这篇稿子于查找资料学习到写出来,差不多花了本人点儿个周未的流年,希望会吃需要之丁要感兴趣想使不断学习的食指或多或少增援(不管是通向前方修,还是向后学)最后还要谢@田园里面的蟋蟀,在看的当儿吃我找了一部分错别字!

援 & 扩展阅读

http://blogs.msdn.com/b/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx
http://blog.stevensanderson.com/2008/04/05/improve-scalability-in-aspnet-mvc-using-asynchronous-requests
http://blogs.msdn.com/b/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx 
http://blogs.msdn.com/b/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx
http://mohamadhalabi.com/2014/05/08/thread-throttling-in-iis-hosted-wcf-sync-vs-async/
Pro Asynchronous Programs with .NET by Richard Blewett and Andrew Clymer

【转】你知道C#中的Lambda表达式的演化过程吗?

【转】你知道C#面临的Lambda表达式的演化过程吗?

那得从十分悠久很久以前说从了,记得很时候…

稀里糊涂的记从前生个让委托的事物是那么的胜深难理解。

委托的下

例一:

图片 1

嗬是信托?

个体掌握:所以来传递方式的种类。(因此来传递数字的品类有int、float、double,用来传递方式的就是来嘱托

例二:

图片 2

并且通过了老大遥远很久…

匿名方式

过剩时分委托接收的章程是一次性的或者方法体是非常简单的…

例三:

图片 3

咱们好形容成:

图片 4

发生没出察觉我们每次都要定义委托,很多上签名可能是相同的。这样便从来不必要定义再度的。

然后还要过了怪悠久很久…

Func和Action

唯恐非常也认为我们每次定义委托有接触痴,所以索性在框架内一样软定义好有可能就此到的信托。那变幻的方签名怎么可能定义得咸?没关系,定义成泛型的莫就是可了也。

先说Func:

图片 5

细之心上人或者看到了,Func相对于AddDelegate多定义了一个int。多出了底坏是乘的凡归路。我们F12扣对应之概念:

图片 6

至于地方Func的写法我们好简写成:(语法糖而已,编译后抑注释的代码

图片 7

再看Action:

图片 8

提示:以后如我们形容代码的下如果写到到delegate…,你要是立马想到是否好为此Func或者Action来取代吗?C#4蒙的Action和Func有16只参数,足够你用了。

咱俩当了以等,又过了好久好久…

Lambda的诞生

图片 9

自家XX,这TM就是亲自兄弟啊。直接去丢delegate关键字,然后加一个=>就成为了lambda表达式了。(=>读作goes to

咱继承简化:

图片 10

丢掉参数类型为是可的,因为强大的VS可以依据泛型委托Func自己想出来参数类型。

尚得简化为?当然:

图片 11

return关键字为决不了,大括哀号也决不了。(当然,方法体只生单纯条告句才能够怎么开

现在扣起就不行接近我们平素于是底Lambda表达式了。

要传入参数就来一个吧,我们还好继承简化:

图片 12

立就是咱平常呈现得极其多的lambda长相了。

一旦长大这样吗是产生求的:

  1. 不得不发出一个传出参数
  2. 方法体只能只同修语句。

至于第1点,lambda我们平常所以得比多之是冲IEnumerable或IQueryable,所以不得不发出一个参数为便满足了。

有关第2点,我们利用扩展方法的链式编程来解决。

如:(用链式来化解第二碰)

图片 13

事后,我们了上了福之生活…

 

借《深入理解C#》中之等同图:

图片 14

 

小知识:(异步Lambda)

Func<Student, Task<bool>> func = async t =>
{
    await Task.Delay(100);//等待100毫秒
    return false;
};

 

结束:

本文简短的说了下lambda表达式在C#屡遭的一步步演化。说之未必然对,轻拍!

本文为同到《C#基础知识巩固系列》

个人档案【转】你必知道之EF知识和涉

【转】你必须掌握的EF知识及经验

注意:以下内容如果没有特别说明,默认使用的EF6.0版本,code first模式。

推荐MiniProfiler插件

工欲善其事,必先利其器。

咱们用EF和在深要命程度增长了支出速度,不过就带动的是无数性质低下的写法和浮动不顶高速之sql。

虽咱可采用SQL Server
Profiler来监控实施之sql,不过个人认为就是麻烦,每次用打开、过滤、清除、关闭。

以这边强烈推荐一个插件MiniProfiler。实时监控页面请求对许实行之sql语句、执行时间。简单、方便、针对性强。

如图:(具体使用和介绍请走)

个人档案 1

数据准备

新建实体:Score(成绩分数表)、Student(学生说明)、Teacher(老师表)

个人档案 2

末端会于出demo代码下充斥链接

foreach循环的陷进 

1.有关延迟加载

个人档案 3

告看上图红框。为什么StudentId有价,而Studet为null?因为用code
first,需要装导航属性也virtual,才见面加载延迟加载数据。

个人档案 4

2.关于在循环中做客导航属性之老大处理(接着上面,加上virtual后会见报以下很)

“已发出开拓的同此 Command 相关联的
DataReader,必须首先以她倒闭。”

个人档案 5

化解方案:

  • 方案1、设定ConnectionString加上MultipleActiveResultSets=true,但才适用于SQL
    2005随后的本子
  • 方案2、或者先念来放置于List中

3.之上两沾单为热身,我们说之陷阱才刚刚开始!

个人档案 6

下一场我们点击打开MiniProfiler工具(不要让吓到)

个人档案 7

个人档案 8

解决方案:使用Include著连续查询(注意:需要手动导入using System.Data.Entity
不然Include只能传表名字符串)。

个人档案 9

又看MiniProfiler的督察(瞬间101久sql变成了1久,这之中的属性可想而知。)

个人档案 10

AutoMapper工具

上面我们透过Include显示的执行表的连查询显然是毋庸置疑的,但还不够。如果我们无非待查询数据的一些字段呢,上面查询所有字段岂不是大浪费内存存储空间和应用程序与数据库数据传带富。

俺们好:

个人档案 11

针对许监督及之sql:

个人档案 12

咱看到变化的sql,查询的字段少了累累。只有咱来得列出来字段的与一个StudentId,StudentId用来连续查询条件的。

是,这样的法门要命不错。可是有没产生什么更好的方案或措施也?答案是早晚的。(不然,也未会见以这边屁话了。)如果表字段非常多,我们要用的字段也颇多,导航属性为老多之时段,这样的手动映射就显示不那么好看了。那么接下我们初步介绍以AutoMapper来就投:

顾:首先得NuGet下载AutoMapper。(然后导入命名空间 using
AutoMapper; using AutoMapper.QueryableExtensions;)

个人档案 13

个人档案 14

咱俩视地方查询语句没有一个个之手动映射,而映射都是单独布置了。其中CreateMap应该是使描绘及Global.asax文件中的。(其实也尽管是分开了照部分,清晰了询问语句。细心的同桌或注意到了,这种措施尚未去了积极性Include)

个人档案 15

我们看出了变的sql和前面有多少见仁见智,但只大成了扳平长条sql,并且结果吗是不利的。(其实就是是多了同样漫漫CASE WHEN ([Extent2].[Id] IS
NOT NULL) THEN 1 END AS
[C1]。看起就长达告句子并从未呀实际意义,然而当下是AutoMapper生成的sql,同时自也表示未理解为什么和EF生成的例外)

这样做的益处?

  1. 避以循环中做客导航属性多次履sql语句。
  2. 避了查询语词被最好多的手动映射,影响代码的读书。

有关AutoMapper的其他有素材:

http://www.cnblogs.com/xishuai/p/3712361.html

http://www.cnblogs.com/xishuai/p/3700052.html

http://www.cnblogs.com/farb/p/AutoMapperContent.html

联表查询统计

要求:查询前100单学生考项目(“模拟考试”、“正式考试”)、考试次数、语文平均分、学生姓名,且考试次数超过等于3赖。(按考试项目分类统计)

代码如下:

个人档案 16

看来这般的代码,我第一反响是灾难性了。又于循环执行sql了。监控如下:

个人档案 17

事实上,我们只是需要有些改变就将101长达sql变成1长达,如下:

个人档案 18

马上变1条。

个人档案 19

俺们开拓查看详细的sql语句

个人档案 20

发现就仅仅只是查询结果集合而已,其中的依考试类别来统计是先后将到具备数据后以算的(而无是以数据库内计算,然后直接返回结果),这样平等是荒废了数据库查询数据传。

至于连接查询分组统计我们得以使用SelectMany,如下:

个人档案 21

监控sql如下:(是免是简单多了吗?)

个人档案 22

关于SelectMany资料:

http://www.cnblogs.com/lifepoem/archive/2011/11/18/2253579.html

http://www.cnblogs.com/heyuquan/p/Linq-to-Objects.html

性提升的AsNonUnicode

个人档案 23

督察到的sql

个人档案 24

我们看EF正常情况变化的sql会于前面带齐“N”,如果我们抬高DbFunctions.AsNonUnicode生成的sql是无“N”的,当你发觉带来达“N”的sql比没带来“N”的
sql查询速度放缓很多底上那么就是亮该怎么处置。

(以前用oracle的时候带不带“N”查询效率差别特别扎眼,今天之所以sql
server测试并不曾发觉什么区别个人档案 25。还有我意识EF6会根据数据库被凡是nvarchar的时才会生成带“N”的sql,oracle数据库没测试,有趣味的校友可以测试下)

特性提升的AsNoTracking

个人档案 26

咱看变化的sql

个人档案 27

sql是生成的等同型一样,但是实施时间也是4.8倍。原因仅仅只是第一长EF语词多加了一个AsNoTracking。

注意:

  • AsNoTracking干啊的吗?无跟踪查询而已,也就是说查询出来的对象不克直接开修改。所以,我们于召开多少集合查询显示,而同时休待对聚集修改并创新到数据库的时,一定毫无遗忘加上AsNoTracking。
  • 假定查询过程做了select映射就不需要加AsNoTracking。如:db.Students.Where(t=>t.Name.Contains(“张三”)).select(t=>new
    (t.Name,t.Age)).ToList();

基本上字段组合排序(字符串)

渴求:查询名字里含有“张三”的学员,先以名排序,再依年龄排序。

个人档案 28

个人档案 29

哟,不对啊。按名排序为年龄排序覆盖了。我们该为此ThenBy来组合排序。

个人档案 30

个人档案 31

不错不错,正是我们怀念使的效益。如果您不思就此ThenBy,且还是升序的语,我们啊可:

个人档案 32

个人档案 33

转的sql是相同的。与OrderBy、ThenBy对应之降序有OrderByDescending、ThenByDescending。

好像好像挺完善了。其实不然,我们大部分景象排序是动态的。比如,我们见面越加前端页面不同之操作要求不同字段的两样排序。那我们后台应该怎么开啊?

个人档案 34

本来,这样好是没问题之,只要您肯。可以这么多或者的论断出无产生发特别SB?是的,我们本有再度好的化解方案。要是OrderBy可以一直传字符串???

解决方案:

  1. guget下载System.Linq.Dynamic 
  2. 导入System.Linq.Dynamic命名空间
  3. 编排OrderBy的恢宏方法

个人档案 35

下一场上面又增长而臭的代码可以形容成:

个人档案 36

咱看下转移的sql:

个人档案 37

暨咱们怀念如果的力量完全符合,是免是感到美美哒!!

【注意】:流传的排序字段后面要加排序关键字
asc或desc

lamdba条件构成

要求:根据不同情形询问,可能情况

  1. 查询name=“张三” 的享有学员
  2. 查询name=“张三” 或者 age=18底具备学员

落实代码:

个人档案 38

举凡匪是味及了平等的荤个人档案 39。下面我们来活组装Lamdba条件。

釜底抽薪方案:

个人档案 40个人档案 41

顿时段代码我啊是从网上偷的,具体链接找不顶了。

然后我们的代码可以描绘成:

个人档案 42

来没有来得意美哒一点个人档案 43。然后我们看看生成的sql是否是:

个人档案 44

EF的预热

http://www.cnblogs.com/dudu/p/entity-framework-warm-up.html

count(*)被您用异常了呢(Any的用法)

渴求:查询是否存在名字呢“张三”的生。(你的代码会怎样写吗?)

个人档案 45

先是种植?第二种?第三种?呵呵,我原先就是是用的首先种植,然后有人说“你count被您用很了”,后来自己眷恋了纪念了怎么就给自己之所以生了呢?直到对比了这三个告知句的性质后自己明白了。

个人档案 46

属性的异竟有三百基本上倍增,count确实于自己为此好了。(我思,不止于自己一个人数所以老了吧。)

咱们看出上面的Any干嘛的?官方解释是:

个人档案 47

自数读者中文说,一直无法了解。甚至早有人为提出了同样的问题《实在看无懂MSDN关于
Any
的说》

从而我个人理解吧是“确定集合中是否发生素满足某平规范”。我们来看望any其他用法:

渴求:查询教了“张三”或“李四”的教工

兑现代码:

个人档案 48

简单种植方法,以前我会习惯写第一种。当然我们看看那个成了之sql和实行效率之后,看法改变了。

个人档案 49

频率的差竟有近六倍

咱俩重对照下count:

个人档案 50

个人档案 51

得出奇怪的定论:

  1. 于导航属性之中用count和用any性能分不慌,反而FirstOrDefault()
    != null的方式性能最好差。
  2. 在一直性判断其中any和FirstOrDefault() !=
    null性能分不要命,count性能使差的多。
  3. 故此,不管是直接性还是导航属性我们还用any来判定是否有是不过稳妥的。

透明标识符

假定由于各种缘由我们用写下面这样逻辑的说话

个人档案 52

咱得以形容成这样更好

个人档案 53

看生成的sql就知道了

个人档案 54

仲种植方式变的sql要彻底得差不多,性能为重新好。

EntityFramework.Extended

此推荐下插件EntityFramework.Extended,看了生,很对。

无限可怜之独到之处就是是足以一直批量改、删除,不用像EF默认的得事先做询问操作。

关于官方EF为什么没有供这么的支持即无知底了。不过使用EntityFramework.Extended需要专注以下几点:

  1. 只支持sql server
  2. 批量修改、删除时无可知兑现工作(也便是生了特别不克回滚)
  3. 没有联级删除
  4. 不能同EF一起SaveChanges
    (详见)

http://www.cnblogs.com/GuZhenYin/p/5482288.html

当斯正个问题EntityFramework.Extended并无是说非克回滚,感谢@GuZhenYin园友的指正(原谅自己之前没动手测试)。

在意:需要NuGet下载EntityFramework.Extended,
并导入命名空间: using
EntityFramework.Extensions ;

测试代码如下:(如果注释掉手抛大代码是足以直接更新到数据库的)

using (var ctxTransaction = db.Database.BeginTransaction())
{
    try
    {
        db.Teachers.Where(t => true).Update(t => new Teacher { Age = "1" });

        throw new Exception("手动抛出异常");

        ctxTransaction.Commit();//提交事务
    }
    catch (Exception)
    {
        ctxTransaction.Rollback();//回滚事务
    }
}

自打定义IQueryable扩展方法

 最后整理下由定义之IQueryable的扩大。

 个人档案 55

个人档案 56

 

补充1:

First和Single的区别:前者是TOP(1)后者是TOP(2),后者如果查询到了2条数据则抛出异常。所以在必要的时候使用Single也不会比First慢多少。

补充2: 

一度打包nuget提供直接装 Install-Package
Talk.Linq.Extensions 或nuget搜索 Talk.Linq.Extensions 

https://github.com/zhaopeiym/Talk/wiki/Talk.Linq.Extensions_demo

 

结束:

源码下载:http://pan.baidu.com/s/1o8MYozw

本文为协同到《C#基础知识巩固系列》

迎热心园友补充!

澳门新葡亰官网【转】权限管理上 一、ASP.NET Forms身份验证

【转】权限管理上 一、ASP.NET
Forms身份证明

说明:本文示例使用的VS2017暨MVC5。
系统无论大小、牛逼或屌丝,一般都距不起来注册、登录。那么连下去我们不怕来分析下用户身份验证。

简而言之实现登录、注销

原先当学习.net的上不懂得呀Forms身份证明,直接用session实现登录,效果啊格外好嘛。而且用户信息存在服务端,安全。
前者代码:

@if (string.IsNullOrWhiteSpace(ViewBag.UserName))
{
    <form action="/home/login1">
        <input type="text" name="userName" />
        <input type="submit" value="登录" />
    </form>
}
else
{
    <form action="/home/logout1">
        <div>当前用户已登录,登录名:@ViewBag.UserName</div>
        <input type="submit" value="退出" />
    </form>
}

后台代码:

public ActionResult Index()
{
    ViewBag.UserName = Session["userName"]?.ToString();           
    return View();
}       

public void Login1(string userName)
{
    if (!string.IsNullOrWhiteSpace(userName))  //为了方便演示,就不做真的验证了     
        Session["userName"] = userName;
    else
        Session["userName"] = null;
    Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原来页面
}

public void Logout1()
{
    Session["userName"] = null;
    Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原来页面
}

是无是,简单明了。想只要和谐扩大或是定制什么力量还颇好用。不过我们用保护session。比如系统又颁发,或者iis被活动重新开。就会出现session丢失的景。也就是用户会莫名其妙提升需要更登录。体验颇不好。(这里先不讨论session服务同数据库的状态)。既然微软出同等套成熟之权杖管理我们怎么不用为?

Forms认证登录、注销

首先在web.config里开Forms身份认证:

<system.web>
  <authentication mode="Forms"></authentication>

后台代码:

public void Login2(string userName)
{
    if (!string.IsNullOrWhiteSpace(userName))  //为了方便演示,就不做真的验证了
        FormsAuthentication.SetAuthCookie(userName, true); //登录
    Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原来页面
}

public void Logout2()
{
    FormsAuthentication.SignOut();//登出
    Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原来页面
}

前台代码:

@if (!Request.IsAuthenticated)
{
    <form action="/home/login2">
        <input type="text" name="userName" />
        <input type="submit" value="登录" />
    </form>
}
else
{
    <form action="/home/logout2">
        <div>当前用户已登录,登录名:@Context.User.Identity.Name</div>
        <input type="submit" value="退出" />
    </form>
}

诸如此类几句代码就兑现了咱的报到和取消。和我们团结一心之所以session管理登录不同。Forms身份证明是直接把消息存cookie到浏览器的。通过SetAuthCookie这个法子名吧足以关押出来。不过Cookie信息通过了加密。
此出必不可少说明session和cookie的涉及。当我们以session来维系用户状态的时光,其实为使了cookie。
澳门新葡亰官网 1
但Forms身份证明仅仅只是把信息存了cookie,而并未以服务端维护一个遥相呼应之session。
未迷信你可测试。可以为此有限种植方式都登录,然后去掉session就可测量出来了。(怎么清session?重开iis,或者修改下后台代码在更编译访问)
【说明】用户征为什么要存cookie?因为HTTP是一个凭状态的磋商。对于服务器来说,每次要都是如出一辙的。所以,只能通过每次要带的cookie来鉴别用户了。(暂时不考虑任何措施)

自定义的地位验证标识

面下的记名很简短,但其实状况屡屡非常复杂。明显正常作业需要存的用户信息会要重复多。那么我们是不是可扩展身份标识也?答案是必定的。
后台代码:

public void Login3(string userName)
{
    if (!string.IsNullOrWhiteSpace(userName))  //为了方便演示,就不做真的验证了     
    {
        UserInfo user = new UserInfo()
        {
            Name = userName,
            LoginTime = DateTime.Now
        };
        //1、序列化要保存的用户信息
        var data = JsonConvert.SerializeObject(user);

        //2、创建一个FormsAuthenticationTicket,它包含登录名以及额外的用户数据。
        FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddDays(1), true, data);

        //3、加密保存
        string cookieValue = FormsAuthentication.Encrypt(ticket);

        // 4. 根据加密结果创建登录Cookie
        HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue);
        cookie.HttpOnly = true;
        cookie.Secure = FormsAuthentication.RequireSSL;
        cookie.Domain = FormsAuthentication.CookieDomain;
        cookie.Path = FormsAuthentication.FormsCookiePath;

        // 5. 写登录Cookie
        Response.Cookies.Remove(cookie.Name);
        Response.Cookies.Add(cookie);
    }
    Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原来页面
}

然后在Global.asax的Application_AuthenticateRequest方法:

protected void Application_AuthenticateRequest()
{
    GetUserInfo();
}

//通过coolie解密 读取用户信息到 HttpContext.Current.User
public void GetUserInfo()
{
    // 1. 读登录Cookie
    HttpCookie cookie = Request.Cookies[FormsAuthentication.FormsCookieName];

    try
    {
        UserInfo userData = null;
        // 2. 解密Cookie值,获取FormsAuthenticationTicket对象
        FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);

        if (ticket != null && string.IsNullOrEmpty(ticket.UserData) == false)
            // 3. 还原用户数据
            userData = JsonConvert.DeserializeObject<UserInfo>(ticket.UserData);

        if (ticket != null && userData != null)
            // 4. 构造我们的MyFormsPrincipal实例,重新给context.User赋值。
            HttpContext.Current.User = new MyFormsPrincipal<UserInfo>(ticket, userData);
    }
    catch { /* 有异常也不要抛出,防止攻击者试探。 */ }
}

前者代码:

@{
    MyFormsPrincipal<UserInfo> user = Context.User as MyFormsPrincipal<UserInfo>;
    if (user == null)
    {
        <form action="/home/login3">
            <input type="text" name="userName" />
            <input type="submit" value="登录" />
        </form>
    }
    else
    {

        <form action="/home/logout2">
            <div>当前用户已登录,登录名:@Context.User.Identity.Name</div>
            <div>当前用户已登录,登录时间:@user.UserData.LoginTime</div>
            <input type="submit" value="退出" />
        </form>
    }
}

实质上整个过程及FormsAuthentication.SetAuthCookie(userName, true); //登录是均等的。只是我们通过扩大,存了我们怀念要存储的数。
过程吧比较简单:

  • 结构要存储的数码
  • 序列化
  • 拿序列化信息放入FormsAuthenticationTicket对象
  • 经FormsAuthentication.Encrypt加密对象
  • 发送cookie到浏览器

此处小复杂点的地方便是解密然后给User赋值HttpContext.Current.User = new MyFormsPrincipal<UserInfo>(ticket, userData);
MyFormsPrincipal需要实现接口MyFormsPrincipal

public class MyFormsPrincipal<TUserData> : IPrincipal where TUserData : class, new()
{
    private IIdentity _identity;
    private TUserData _userData;

    public MyFormsPrincipal(FormsAuthenticationTicket ticket, TUserData userData)
    {
        if (ticket == null)
            throw new ArgumentNullException("ticket");
        if (userData == null)
            throw new ArgumentNullException("userData");

        _identity = new FormsIdentity(ticket);
        _userData = userData;
    }

    public TUserData UserData
    {
        get { return _userData; }
    }

    public IIdentity Identity
    {
        get { return _identity; }
    }

    public bool IsInRole(string role)//这里暂时不实现
    {
        return false;
    }
}

反倒也尚未呀特别,就是实例化的时段传出票据及于定义数据就是吓了。

授权

发了登录一般还去不开授权。微软的物好就好以,一般还是全成套的。

[Authorize]
public ActionResult LoginOk()
{
    return View();
}

直白给Action添加一个Authorize特性就哼了,这人便会见活动检查是否登录。如果无登录活动跳反到登录页面。登录页面的装置或在web.config里面

<system.web>
  <authentication mode="Forms" >
    <forms loginUrl="/home/index"></forms>

这种概括的授权验证澳门新葡亰官网明显是不够的。很多时节某些页面只有某些人才会看。比如VIP。那么我们还要使扩大了。

//继承 AuthorizeAttribute
public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.User.Identity.Name != "农码一生")
        {
            filterContext.HttpContext.Response.Write("您不是vip用户,不能访问机密数据");
            filterContext.HttpContext.Response.End();
            return;
        }
        base.OnAuthorization(filterContext);
    }
}

[MyAuthorize]
public ActionResult LoginVIP()
{
    return View();
}

对,就是这么简单。说了如此多,来张效果图吧:
澳门新葡亰官网 2

 

推荐阅读:

  • http://www.cnblogs.com/fish-li/archive/2012/04/15/2450571.html
    Demo:
  • https://github.com/zhaopeiym/BlogDemoCode/tree/master/权限管理/1-Forms身份认证

 

【转】Linq表达式、Lambda表达式你再度爱好哪个?

【转】Linq表达式、Lambda表达式你再爱好哪个?

什么是Linq表达式?什么是Lambda表达式?

如图:

个人档案 1

有鉴于此Linq表达式和Lambda表达式并无呀可比性

那么和Lambda表达式相关的整条语句称作什么呢?在微软连没被起官方的命名,在《深入理解C#》中称为点标记

个人档案 2

询问表达式、点标记你又爱谁?

从而,我们的题目的问讯根本就无相宜。应该是“询问表达式和点标记你再爱哪个?”。如:

 //查询表达式
 var students1 = from t in db.Students
                where t.Name == "张三"
                select new { t.Id, t.Name, t.Age };
 //点标记
 var students2 = db.Students
                 .Where(t => t.Name == "张三")
                 .Select(t => new { t.Id, t.Name, t.Age });

为何选择点标记 

本人相信又多的食指偏偏于选择点标记。具体什么原因我吗说不清(可能是点标记中的Lambda更加文雅吧)。对于自个人来说,也是更喜欢点标记这种方法。

1、所有的查询表达式都可以更改成对应之触发标记。反之,不是所有的点标记都得变动成查询表达式。

何以?因为查询表达式在编译后即直接成为了点标记:(以下是上面两独话对应的编译后底反编译C#代码)

个人档案 3

转了一致模子一样的代码。(由于是编译后底,好多乱七八糟七八糟的代码。我们只是拘留Where和Select关键字就是亮,使用的还是接触标记。)

2、点标记确实于查询表达式更加文雅

例一:

 //查询表达式
 var students1 = from t in db.Students
                 where t.Name == "张三"
                 select t;
 //点标记
 var students2 = db.Students
                 .Where(t => t.Name == "张三");

自己为什么一定要 select
好家伙,这句没卵用的废话就未能够看吗?是的,省了便报错:

个人档案 4

例二:

总得得括号包裹起来才会得到结果集?你还会再次可恶一点啊?

//查询表达式
var students1 = (from t in db.Students
                 where t.Name == "张三"
                 select t).ToList();
//点标记
var students2 = db.Students
                .Where(t => t.Name == "张三")
                .ToList();       

例三:(为什么说:”不是怀有的触发标记都可改变成为查询表达式”【此例只适用于IEnumerator】)

此条点标记你会转成为查询表达式吗?

var list = new List<string>() { "张三", "张三", "张三", "张三", "李四", "张三", "李四", "张三", "李四" };

var students2 = list
                .Where((item, index) => item == "张三" && index % 2 == 0)
                .Select((item, index) => new { item, index })
                .ToList();

询问表达式你可知Reverse吗?

var list = new List<string>() { "张三1", "张三2", "张三3", "张三0", "李四9", "张三3", "李四", "张三2", "李四" };

var students2 = list
             .Where((item, index) => item.Contains("张三"))
             .Select((item, index) => new { item, index })
             .Reverse()//反序
             .ToList();

ListA.Distinct().ToList();//去重
ListA.Except(ListB).ToList();//差集
ListA.Union(ListB).ToList();  //并集
ListA.Intersect(ListB).ToList();//交集

 

哎时用查询表达式?

透过上面的对照,好像查询表达式一文不值了。no,不是这样的。

据下面几乎种状况我们虽好选下查询表达式:

例一:本例适用于Linq to Object 和
没有建主外键的EF查询

点标记中之Join需要传四个参数表达式,是未是产生点晕了。。。

var list1 = new Dictionary<string, string> { { "1", "张三" }, { "2", "李四" }, { "3", "张三" }, { "4", "张三" } };
var list2 = new Dictionary<string, string> { { "1", "张三" }, { "2", "李四" }, { "3", "李四" }, { "4", "张三" } };

//查询表达式
var obj1 = from l1 in list1
           join l2 in list2
           on l1.Key equals l2.Key
           select new { l1, l2 };
//点标记
var obj = list1.Join(list2, l1 => l1.Key, l2 => l2.Key, (l1, l2) => new { l1, l2 });

例二:

接触标记需要区分OrderBy、ThenBy有没出觉得费事

//查询表达式
var obj1 = from l1 in list1
           join l2 in list2
           on l1.Key equals l2.Key
           orderby l1.Key, l2.Key descending
           select new { l1, l2 };
//点标记
var obj = list1.Join(list2, l1 => l1.Key, l2 => l2.Key, (l1, l2) => new { l1, l2 })
    .OrderBy(li => li.l1.Key)
    .ThenByDescending(li => li.l2.Key)
    .Select(t => new { t.l1, t.l2 });

毕竟认为查询表达式更多之无非是为着照顾那些状惯了sql的程序员。

连着查询(内联、左联、交叉联)

关于联接查询利用查询表达式个人档案会再也合适一些此地方就说了。

通下去我们描绘内联、左联、交叉联的询问表达式和呼应的触及标记代码。(目的:可能小人不见面,同时在此间吧被协调举行只备忘)

内联:

个人档案 5

左联:

个人档案 6

交叉联:

个人档案 7

事实上关于联接查询,如果EF建好了主外键我要觉得点标记用起来又爽爽的。

 

结束:

正文并无是一旦反您的习惯,也非是否定而的眼光。仅仅只是表达个人对碰标记和询问表达式的有点理解。

关于凡运查询表达式还是接触标记,可能打在重新不行决定性的用意的凡团队共同的惯与正式。

下一场还惦记说说,只要我们相比什么,很可能就会有人跳出了,什么绝不比,用好了都一致,什么什么才是无比关键的,什么啊的。。。

就如许多丁见面倒感java和C#的对立统一,其实自己个人认为比下底层实现、对比下语法简易也未是无可以的,只要我们得从中学到文化(个人为是勿喜欢对比
谁哪个哪个模仿啊工资稍多少)。

昨日的要好相比今天之好,今天之团结比明天底团结。只要可以进步为什么不用对比呢?

 

正文为协同到《C#基础知识巩固系列》

【转】C#异步的世界【上】

【转】C#异步的社会风气【上】

初进阶的程序员可能对async、await用得较多,却对之前的异步了解非常少。本人就是是此类,因此打算回顾上下异步的进化史。 

正文主要是回顾async异步模式之前的异步,下篇文章又来要分析async异步模式。

APM

APM 异步编程模型,Asynchronous Programming Model

早在C#1之时节就是产生矣APM。虽然未是特别熟悉,但是有些要展现了之。就是那些看似是BeginXXX和EndXXX的法门,且BeginXXX返回值是IAsyncResult接口。

于业内写APM示例之前我们先行让来一致截联机代码

//1、同步方法
private void button1_Click(object sender, EventArgs e)
{          
    Debug.WriteLine("【Debug】线程ID:" + Thread.CurrentThread.ManagedThreadId);

    var request = WebRequest.Create("https://github.com/");//为了更好的演示效果,我们使用网速比较慢的外网
    request.GetResponse();//发送请求    

    Debug.WriteLine("【Debug】线程ID:" + Thread.CurrentThread.ManagedThreadId);
    label1.Text = "执行完毕!";
}

【说明】为了重新好之演示异步效果,这里我们使用winform程序来开示范。(因为winform始终犹需UI线程渲染界面,如果让UI线程占用则会油然而生“假死”状态)

【效果图】

个人档案 1

关押图得知:

  • 咱们于推行措施的下页面出现了“假死”,拖不动了。
  • 咱看到打印结果,方法调用前与调用后线程ID还是9(也尽管是跟一个线程)

下我们再来演示对应之异步方法:(BeginGetResponse、EndGetResponse所谓的APM异步模型)

private void button2_Click(object sender, EventArgs e)
{
    //1、APM 异步编程模型,Asynchronous Programming Model
    //C#1[基于IAsyncResult接口实现BeginXXX和EndXXX的方法]             
    Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);

    var request = WebRequest.Create("https://github.com/");
    request.BeginGetResponse(new AsyncCallback(t =>//执行完成后的回调
    {
        var response = request.EndGetResponse(t);
        var stream = response.GetResponseStream();//获取返回数据流 

        using (StreamReader reader = new StreamReader(stream))
        {
            StringBuilder sb = new StringBuilder();
            while (!reader.EndOfStream)
            {
                var content = reader.ReadLine();
                sb.Append(content);
            }
            Debug.WriteLine("【Debug】" + sb.ToString().Trim().Substring(0, 100) + "...");//只取返回内容的前100个字符 
            Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
            label1.Invoke((Action)(() => { label1.Text = "执行完毕!"; }));//这里跨线程访问UI需要做处理
        }
    }), null);

    Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId); 
}

【效果图】

 个人档案 2

在押图得知:

  • 启用异步方法并从未是UI界面卡死
  • 异步方法启动了另外一个ID为12之线程

方代码执行顺序:

个人档案 3

眼前我们说过,APM的BebinXXX必须返回IAsyncResult接口。那么连下我们解析IAsyncResult接口:

首先我们看:

个人档案 4

确实返回的凡IAsyncResult接口。那IAsyncResult到底长的哟体统?:

个人档案 5

连没想像着之那复杂嘛。我们是不是好尝试这贯彻之接口,然后显示自己的异步方法也?

率先肯定一个类MyWebRequest,然后继续IAsyncResult:(下面是着力的伪代码实现)

public class MyWebRequest : IAsyncResult
{
    public object AsyncState
    {
        get { throw new NotImplementedException(); }
    }

    public WaitHandle AsyncWaitHandle
    {
        get { throw new NotImplementedException(); }
    }

    public bool CompletedSynchronously
    {
        get { throw new NotImplementedException(); }
    }

    public bool IsCompleted
    {
        get { throw new NotImplementedException(); }
    }
}

这般自然是不克用底,起码也得有个存回调函数的属性吧,下面我们多少改造下:

个人档案 6

然后我们可从定义APM异步模型了:(成对的Begin、End)

public IAsyncResult MyBeginXX(AsyncCallback callback)
{
    var asyncResult = new MyWebRequest(callback, null);
    var request = WebRequest.Create("https://github.com/");
    new Thread(() =>  //重新启用一个线程
    {
        using (StreamReader sr = new StreamReader(request.GetResponse().GetResponseStream()))
        {
            var str = sr.ReadToEnd();
            asyncResult.SetComplete(str);//设置异步结果
        }

    }).Start();
    return asyncResult;//返回一个IAsyncResult
}

public string MyEndXX(IAsyncResult asyncResult)
{
    MyWebRequest result = asyncResult as MyWebRequest;
    return result.Result;
}

调用如下:

 private void button4_Click(object sender, EventArgs e)
 {
     Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);
     MyBeginXX(new AsyncCallback(t =>
     {
         var result = MyEndXX(t);
         Debug.WriteLine("【Debug】" + result.Trim().Substring(0, 100) + "...");
         Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
     }));
     Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);
 }

效果图:

个人档案 7

咱看自己实现之法力基本上与体系提供的大半。

  • 启用异步方法并没是UI界面卡死
  • 异步方法启动了另外一个ID为11底线程

【总结】

个体认为APM异步模式就是是启用另外一个线程执行耗时任务,然后通过回调函数执行后续操作。

APM还得透过外方获得值,如:

while (!asyncResult.IsCompleted)//循环,直到异步执行完成 (轮询方式)
{
    Thread.Sleep(100);
}
var stream2 = request.EndGetResponse(asyncResult).GetResponseStream();

asyncResult.AsyncWaitHandle.WaitOne();//阻止线程,直到异步完成 (阻塞等待)
var stream2 = request.EndGetResponse(asyncResult).GetResponseStream();

 

上:如果是屡见不鲜方法,我们吧可经过信托异步:(BeginInvoke、EndInvoke)

 public void MyAction()
 {
     var func = new Func<string, string>(t =>
     {
         Thread.Sleep(2000);
         return "name:" + t + DateTime.Now.ToString();
     });

     var asyncResult = func.BeginInvoke("张三", t =>
     {
         string str = func.EndInvoke(t);
         Debug.WriteLine(str);
     }, null); 
 }

EAP

EAP 基于事件之异步模式,Event-based Asynchronous Pattern

本条模式在C#2的时光光顾。

事先来拘禁个EAP的例证:

 private void button3_Click(object sender, EventArgs e)
 {            
     Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);

     BackgroundWorker worker = new BackgroundWorker();
     worker.DoWork += new DoWorkEventHandler((s1, s2) =>
     {
         Thread.Sleep(2000);
         Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
     });//注册事件来实现异步
     worker.RunWorkerAsync(this);
     Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);
 }

 

【效果图】(同样不会见阻塞UI界面)

个人档案 8

【特征】

  • 由此波的法门注册回调函数
  • 经过 XXXAsync方法来推行异步调用

事例十分简单,但是同APM模式相比,是无是从未有过那清晰透明。为什么可以这么实现?事件之注册是于关系嘛?为什么执行RunWorkerAsync会触发注册的函数?

倍感温馨同时想多了…

俺们尝试着倒编译看看源码:

个人档案 9

 只想说,这么玩,有意思吗?

TAP

TAP 基于任务的异步模式,Task-based Asynchronous Pattern

顶目前为止,我们以为上面的APM、EAP异步模式好用吗?好像从没意识什么问题。再细致想想…如果我们发多单异步方法要依照先后顺序执行,并且要(在主进程)得到有返回值。

先是定义三单委托:

public Func<string, string> func1()
{
    return new Func<string, string>(t =>
    {
        Thread.Sleep(2000);
        return "name:" + t;
    });
}
public Func<string, string> func2()
{
    return new Func<string, string>(t =>
    {
        Thread.Sleep(2000);
        return "age:" + t;
    });
}
public Func<string, string> func3()
{
    return new Func<string, string>(t =>
    {
        Thread.Sleep(2000);
        return "sex:" + t;
    });
}

下一场照一定顺序执行:

public void MyAction()
{
    string str1 = string.Empty, str2 = string.Empty, str3 = string.Empty;
    IAsyncResult asyncResult1 = null, asyncResult2 = null, asyncResult3 = null;
    asyncResult1 = func1().BeginInvoke("张三", t =>
    {
        str1 = func1().EndInvoke(t);
        Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
        asyncResult2 = func2().BeginInvoke("26", a =>
        {
            str2 = func2().EndInvoke(a);
            Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
            asyncResult3 = func3().BeginInvoke("男", s =>
            {
                str3 = func3().EndInvoke(s);
                Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
            }, null);
        }, null);
    }, null);

    asyncResult1.AsyncWaitHandle.WaitOne();
    asyncResult2.AsyncWaitHandle.WaitOne();
    asyncResult3.AsyncWaitHandle.WaitOne();
    Debug.WriteLine(str1 + str2 + str3);
} 

而外难看、难读一些像样也从不什么 。不过实在是这样为?

个人档案 10

asyncResult2是null?
有鉴于此在好第一单异步操作前从未对asyncResult2进行赋值,asyncResult2执行异步等待的下报好。那么如此我们虽无法控制三个异步函数,按照一定顺序执行好后又用到回值。(理论及还是有另外方之,只是会然代码更加错综复杂)

 

不错,现在该我们的TAP登场了。

个人档案 11

仅需要调用Task类的静态方法Run,即可轻松使用异步。

获返回值:

var task1 = Task<string>.Run(() =>
{
    Thread.Sleep(1500);
    Console.WriteLine("【Debug】task1 线程ID:" + Thread.CurrentThread.ManagedThreadId);
    return "张三";
});
//其他逻辑            
task1.Wait();
var value = task1.Result;//获取返回值
Console.WriteLine("【Debug】主 线程ID:" + Thread.CurrentThread.ManagedThreadId);

今昔咱们处理地方多独异步按顺序执行:

Console.WriteLine("【Debug】主 线程ID:" + Thread.CurrentThread.ManagedThreadId);
string str1 = string.Empty, str2 = string.Empty, str3 = string.Empty;
var task1 = Task.Run(() =>
{
    Thread.Sleep(500);
    str1 = "姓名:张三,";
    Console.WriteLine("【Debug】task1 线程ID:" + Thread.CurrentThread.ManagedThreadId);
}).ContinueWith(t =>
{
    Thread.Sleep(500);
    str2 = "年龄:25,";
    Console.WriteLine("【Debug】task2 线程ID:" + Thread.CurrentThread.ManagedThreadId);
}).ContinueWith(t =>
{
    Thread.Sleep(500);
    str3 = "爱好:妹子";
    Console.WriteLine("【Debug】task3 线程ID:" + Thread.CurrentThread.ManagedThreadId);
});

Thread.Sleep(2500);//其他逻辑代码

task1.Wait();

Debug.WriteLine(str1 + str2 + str3);
Console.WriteLine("【Debug】主 线程ID:" + Thread.CurrentThread.ManagedThreadId);

[效果图]

个人档案 12

俺们看来,结果都收获了,且是异步按次执行的。且代码的逻辑思路特别清晰。如果您感受还无是不行老,那么你现象而是100个异步方法要异步按次序执行吗?用APM的异步回调,那至少也得异步回调嵌套100不成。那代码的复杂度可想而知。

 

拉开思考

  • WaitOne就等的规律

  • 异步为什么会升级性能

  • 线程的运数据以及CPU的使用率有得的关联吗

 

问题1:WaitOne完成等的规律

在此之前,我们先行来概括的了解下基本上线程信号控制AutoResetEvent类。

var _asyncWaitHandle = new AutoResetEvent(false);
_asyncWaitHandle.WaitOne();

其一代码会当 WaitOne 的地方会面直接待下。除非有另外一个线程执行 AutoResetEvent 的set方法。

var _asyncWaitHandle = new AutoResetEvent(false);
_asyncWaitHandle.Set();
_asyncWaitHandle.WaitOne();

然,到了 WaitOne 就得直接执行下去。没有出外等待。

而今咱们本着APM 异步编程模型中之 WaitOne 等待是匪是亮了点啊呢。我们回头来促成之前起定义异步方法的异步等待。

public class MyWebRequest : IAsyncResult
{
    //异步回调函数(委托)
    private AsyncCallback _asyncCallback;
    private AutoResetEvent _asyncWaitHandle;
    public MyWebRequest(AsyncCallback asyncCallback, object state)
    {
        _asyncCallback = asyncCallback;
        _asyncWaitHandle = new AutoResetEvent(false);
    }
    //设置结果
    public void SetComplete(string result)
    {
        Result = result;
        IsCompleted = true;
        _asyncWaitHandle.Set();
        if (_asyncCallback != null)
        {
            _asyncCallback(this);
        }
    }
    //异步请求返回值
    public string Result { get; set; }
    //获取用户定义的对象,它限定或包含关于异步操作的信息。
    public object AsyncState
    {
        get { throw new NotImplementedException(); }
    }
    // 获取用于等待异步操作完成的 System.Threading.WaitHandle。
    public WaitHandle AsyncWaitHandle
    {
        //get { throw new NotImplementedException(); }

        get { return _asyncWaitHandle; }
    }
    //获取一个值,该值指示异步操作是否同步完成。
    public bool CompletedSynchronously
    {
        get { throw new NotImplementedException(); }
    }
    //获取一个值,该值指示异步操作是否已完成。
    public bool IsCompleted
    {
        get;
        private set;
    }
}

辛亥革命代码就是骤增的异步等待。

【执行步骤】

个人档案 13

 

问题2:异步为什么会升级性

按同代码:

Thread.Sleep(10000);//假设这是个访问数据库的方法
Thread.Sleep(10000);//假设这是个访问FQ网站的方法

其一代码用20秒。

如是异步:

var task = Task.Run(() =>
{
    Thread.Sleep(10000);//假设这是个访问数据库的方法
});
Thread.Sleep(10000);//假设这是个访问FQ网站的方法
task.Wait();

诸如此类就设10秒了。这样便省了10秒。

如果是:

var task = Task.Run(() =>
{
    Thread.Sleep(10000);//假设这是个访问数据库的方法
}); 
task.Wait();

异步执行中没有耗时的代码那么这样的异步将凡从来不意思的。

或者:

var task = Task.Run(() =>
{
    Thread.Sleep(10000);//假设这是个访问数据库的方法
}); 
task.Wait();
Thread.Sleep(10000);//假设这是个访问FQ网站的方法

把耗时任务在异步等待后,那这样的代码也是不见面来性能提升的。

还有同种情景:

倘若是单核CPU进行高密集运算操作,那么异步也是从来不意义的。(因为运算是那个耗CPU,而网络要等待不耗CPU)

 

问题3:线程的以数据以及CPU的使用率有必然的关系吗

答案是否。

或拿就对做而。

情况1:

long num = 0;
while (true)
{
    num += new Random().Next(-100,100);
    //Thread.Sleep(100);
}

单核下,我们才启动一个线程,就足以被你CPU爆满。

个人档案 14个人档案 15

起先八次等,八经过CPU基本满员。

情况2:

个人档案 16

个人档案 17

一千大抵单线程,而CPU的使用率还是0。由此,我们获得了前面的下结论,线程的运数据以及CPU的使用率没有早晚的关联。

虽说如此,但是呢无可知不用节制的开启线程。因为:

  • 开启一个初的线程的过程是较耗资源的。(可是使用线程池,来降低开启新线程所吃的资源)
  • 差不多线程的切换为是索要时之。
  • 每个线程占用了必然的内存保存线程上下文信息。

 

demo:http://pan.baidu.com/s/1slOxgnF

正文就联合至索引目录:《C#基础知识巩固》

对于异步编程了解不殊,文中极有或大多介乎错误描述和理念。

谢谢广大园友的指正。

针对相互讨论的目的,绝无想使误导大家的意思。

 

【推荐】

http://www.cnblogs.com/wisdomqq/archive/2012/03/26/2412349.html

【转】再称IQueryable,揭开表达式树的心腹面纱

【转】再张嘴IQueryable<T>,揭开表达式树的秘面纱

接上篇《预先说IEnumerable,我们每天用之foreach你实在了解她吗?》

前不久园里定制好的orm那是一个风生水从,感觉不整自己之orm都非好意思继续混博客园了(开单玩笑)。那么在此之前我们发必不可少仔细了解下 IQueryable<T> ,于是就发生了此文。

哎是培育?

咦是培养?这个题材好像有些白痴。树不纵是树嘛。看图:

个人档案 1

咱打极度下的中坚开始为上看,主枝-分支-分支….可以说凡是无比分支下去。我们倒过来看就是如此:

个人档案 2

平时我们因此得极其多之扶植结构数据就是是XML了,节点下面可以尽上加子节点。我们想平时尚用过啊树结构数据,比如:菜单无限分级、评论区的楼宇。

眼看和我们今天云的发生毛关系啊。… 我们今天要就是是来分析表达式树的。、

lambda表达式和表达式树的区分:

Lambda表达式:

Func<Student, bool> func = t => t.Name == "农码一生";

表达式树: 

Expression<Func<Student, bool>> expression = t => t.Name == "农码一生"; 

 咋一看,没啥区别啊。表达式只是用Expression包了一下耳。那尔擦了,这单是Microsoft给咱展示的障眼法,我们看编译后的C#代码:

个人档案 3

率先只lambda表达式编译成了匿名函数,第二单表达式树编译成一了同积我们不认的事物,远较咱本描绘的lambda复杂得差不多。

结论:

  • 俺们平素采用的表达式树,是编辑的lambda表达式然后编译成的表达式树,也就是说平时相像情形用的表达式树都是编译器帮我们完成的。(当然,我们可得手动的积极的失去创表达式树。只是太累,不是必备情况并未孰愿意去干这个苦活呢)

我们来探视表达式树到底出什么神奇之地方:

个人档案 4

发出没产生看起点感觉来?Body里面来Right、Left,Right里面又发生Right、Left,它们的路且是后续自 Expression 。这种节点下面有节点,可以无限叠加下去的数据结构我们叫树结构数据。也就算是咱的抒发式树。

补:上面的 Student 实体类:

个人档案 5个人档案 6

public class Student
{
    public string Name { get; set; }

    public int Age { get; set; }

    public string Address { get; set; }

    public string Sex { get; set; }
}

View Code

分析表达式树

地方我们看看了所谓的表达式树,其他也绝非设想的那复杂嘛。不就是一个培养结构数据嘛。如果我们而兑现协调的orm,免不了而分析表达式树。一般说到剖析树结构数据都见面就此到递归算法。下面我们初步解析表达式树。

先行定义解析方法:

//表达式解析
public static class AnalysisExpression
{
    public static void VisitExpression(Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.Call://执行方法
                MethodCallExpression method = expression as MethodCallExpression;
                Console.WriteLine("方法名:" + method.Method.Name);
                for (int i = 0; i < method.Arguments.Count; i++)
                    VisitExpression(method.Arguments[i]);
                break;
            case ExpressionType.Lambda://lambda表达式
                LambdaExpression lambda = expression as LambdaExpression;
                VisitExpression(lambda.Body);
                break;
            case ExpressionType.Equal://相等比较
            case ExpressionType.AndAlso://and条件运算
                BinaryExpression binary = expression as BinaryExpression;
                Console.WriteLine("运算符:" + expression.NodeType.ToString());
                VisitExpression(binary.Left);
                VisitExpression(binary.Right);
                break;
            case ExpressionType.Constant://常量值
                ConstantExpression constant = expression as ConstantExpression;
                Console.WriteLine("常量值:" + constant.Value.ToString());
                break;
            case ExpressionType.MemberAccess:
                MemberExpression Member = expression as MemberExpression;
                Console.WriteLine("字段名称:{0},类型:{1}", Member.Member.Name, Member.Type.ToString());
                break;
            default:
                Console.Write("UnKnow");
                break;
        }
    }

}

调用解析方法:

Expression<Func<Student, bool>> expression = t => t.Name == "农码一生" && t.Sex == "男";
AnalysisExpression.VisitExpression(expression);

俺们来看看执行进程:

个人档案 7

如出一辙重叠一重叠的往子节点递归,直到遍历完所有的节点。最后打印效果如下:

个人档案 8

差不多我们怀念使的元素和价值都得到到了,接着怎么组装就扣留你协调的心情了。是拼成sql,还是生成url,请随意!

实现和谐之IQueryable<T>、IQueryProvider

单解析了表达式树就可鼓捣自己之orm了?不行,起码也要是根据 IQueryable<T> 接口来编码吧。

就我们打定义个类 MyQueryable<T> 继承接口 IQueryable<T> :

 public class MyQueryable<T> : IQueryable<T>
 {
     public IEnumerator<T> GetEnumerator()
     {
         throw new NotImplementedException();
     }
     IEnumerator IEnumerable.GetEnumerator()
     {
         throw new NotImplementedException();
     }
     public Type ElementType
     {
         get { throw new NotImplementedException(); }
     }
     public Expression Expression
     {
         get { throw new NotImplementedException(); }
     }
     public IQueryProvider Provider
     {
         get { throw new NotImplementedException(); }
     }
 }

咱见到里面起个接口属性 IQueryProvider ,这个接口的作用非常正在为,主要作用是于实践查询操作符的时候更创设 IQueryable<T> 并且最终遍历的时节实施sql远程取值。我们还看见了 Expression  属性。

如今我们明白了 IQueryable<T> 和 Expression (表达式树)的关联了咔嚓:

  •  IQueryable<T> 最根本的意就是用来存储 Expression(表达式树)

下我们呢从定义现实了 IQueryProvider 接口的类 MyQueryProvider :

public class MyQueryProvider : IQueryProvider
{
    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        throw new NotImplementedException();
    }
    public IQueryable CreateQuery(Expression expression)
    {
        throw new NotImplementedException();
    }
    public TResult Execute<TResult>(Expression expression)
    {
        throw new NotImplementedException();
    }
    public object Execute(Expression expression)
    {
        throw new NotImplementedException();
    }
}

方都是自动生成的伪代码,下面我们来填充具体的实现:

个人档案 9个人档案 10

    public class MyQueryProvider : IQueryProvider
    {
        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
        {
            return new MyQueryable<TElement>(expression);
        }

        public IQueryable CreateQuery(Expression expression)
        {
            throw new NotImplementedException();
        }

        public TResult Execute<TResult>(Expression expression)
        {
            return default(TResult);
        }

        public object Execute(Expression expression)
        {
            return new List<object>();
        } 
    }  
    public class MyQueryable<T> : IQueryable<T>
    {
        public MyQueryable()
        {
            _provider = new MyQueryProvider();
            _expression = Expression.Constant(this);
        }

        public MyQueryable(Expression expression)
        {
            _provider = new MyQueryProvider();
            _expression = expression;
        }
        public Type ElementType
        {
            get { return typeof(T); }
        }

        private Expression _expression;
        public Expression Expression
        {
            get { return _expression; }
        }

        private IQueryProvider _provider;
        public IQueryProvider Provider
        {
            get { return _provider; }
        }

        public IEnumerator GetEnumerator()
        {
            return (Provider.Execute(Expression) as IEnumerable).GetEnumerator();
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            var result = _provider.Execute<List<T>>(_expression);
            if (result == null)
                yield break;
            foreach (var item in result)
            {
                yield return item;
            }
        }
    }

View Code

实施代码:

 var aa = new MyQueryable<Student>();
 var bb = aa.Where(t => t.Name == "农码一生");
 var cc = bb.Where(t => t.Sex == "男");
 var dd = cc.AsEnumerable();
 var ee = cc.ToList(); 

就我们看执行进程:

个人档案 11

结论:

  • 老是在实行 Where 查询操作符的时段 IQueryProvider 会为咱创建一个新的 IQueryable<T> 
  • 调用 AsEnumerable() 方法的时刻并无会见失掉实际取值(只是获得一个IEnumerable)[专注:在EF里面查询不要先取IEnumerable后滤筛,因为AsEnumerable()会转变查询全表的sql]
  • 尽 ToList() 方法时才去真正调用迭代器 GetEnumerator() 取值
  • 诚取值的时刻,会去实施 IQueryProvider 中之 Execute 方法。(就是在调用这个措施的当儿解析表达式数,然后实施得结果)

咱们来看确实当办实事的 Execute
 我们也叫他回回默认值了。

个人档案 12

今昔估算有人不爽了,你顶是切实落实下 Execute 。好吧!(其实通过地方说的剖析表达式树,你可协调以此做纪念做的外事了。)

第一为简单起见,我们因而一个集做啊数据源:

//构造Student数组
public static List<Student> StudentArrary = new List<Student>()
{
        new Student(){Name="农码一生", Age=26, Sex="男", Address="长沙"},
        new Student(){Name="小明", Age=23, Sex="男", Address="岳阳"},
        new Student(){Name="嗨-妹子", Age=25, Sex="女", Address="四川"}
};

然后,重新勾一个VisitExpression2智:(和前的区别:
现在目的是获取表达式树被的表达式,而不是再度组建成sql或别的)

public static void VisitExpression2(Expression expression, ref List<LambdaExpression> lambdaOut)
{
    if (lambdaOut == null)
        lambdaOut = new List<LambdaExpression>();
    switch (expression.NodeType)
    {
        case ExpressionType.Call://执行方法
            MethodCallExpression method = expression as MethodCallExpression;
            Console.WriteLine("方法名:" + method.Method.Name);
            for (int i = 0; i < method.Arguments.Count; i++)
                VisitExpression2(method.Arguments[i], ref  lambdaOut);
            break;
        case ExpressionType.Lambda://lambda表达式
            LambdaExpression lambda = expression as LambdaExpression;
            lambdaOut.Add(lambda);
            VisitExpression2(lambda.Body, ref  lambdaOut);
            break;
        case ExpressionType.Equal://相等比较
        case ExpressionType.AndAlso://and条件运算
            BinaryExpression binary = expression as BinaryExpression;
            Console.WriteLine("运算符:" + expression.NodeType.ToString());
            VisitExpression2(binary.Left, ref  lambdaOut);
            VisitExpression2(binary.Right, ref  lambdaOut);
            break;
        case ExpressionType.Constant://常量值
            ConstantExpression constant = expression as ConstantExpression;
            Console.WriteLine("常量值:" + constant.Value.ToString());
            break;
        case ExpressionType.MemberAccess:
            MemberExpression Member = expression as MemberExpression;
            Console.WriteLine("字段名称:{0},类型:{1}", Member.Member.Name, Member.Type.ToString());
            break;
        case ExpressionType.Quote:
            UnaryExpression Unary = expression as UnaryExpression;
            VisitExpression2(Unary.Operand, ref  lambdaOut);
            break;
        default:
            Console.Write("UnKnow");
            break;
    }
}

接下来再次实现方式 Execute :

public TResult Execute<TResult>(Expression expression)
{
    List<LambdaExpression> lambda = null;
    AnalysisExpression.VisitExpression2(expression, ref lambda);//解析取得表达式数中的表达式
    IEnumerable<Student> enumerable = null;
    for (int i = 0; i < lambda.Count; i++)
    {
        //把LambdaExpression转成Expression<Func<Student, bool>>类型
        //通过方法Compile()转成委托方法
        Func<Student, bool> func = (lambda[i] as Expression<Func<Student, bool>>).Compile(); 
        if (enumerable == null)
            enumerable = Program.StudentArrary.Where(func);//取得IEnumerable
        else
            enumerable = enumerable.Where(func);
    }
    dynamic obj = enumerable.ToList();//(注意:这个方法的整个处理过程,你可以换成解析sql执行数据库查询,或者生成url然后请求获取数据。)
    return (TResult)obj;
}

履进程:

个人档案 13

私对 IQueryable 延迟加载的接头:

  • 前段部分的查询操作符只是将逻辑讲存入表达式树,并从未远程执行sql。
  • foreache执行的凡 IEnumerable<T> ,然而 IEnumerable<T> 同样具有缓加载的特性。每次迭代的时节才真的的得到多少。且以运用导航属性的时刻会再查询数据库。(下次说延迟加载不要忘记了 IEnumerable 的功德哦!)

小知识:

表达式树转成为Lambda个人档案表达式:

Expression<Func<Student, bool>> expression = t => t.Name == "农码一生";
Func<Student, bool> func = expression.Compile();

总结:

发表式树的分析就是停下了,其中还有很多细节要要的尚未分析到。下次产生新的体验再来总结。

感表达式树就是先行把表达式打散存在树结构里(一般打散的历程是编译器完成),然后可以因不同之数据源或接口重新组建成自己想使之别款式,这吗为我们实现和谐之orm成为了或者。

今天重中之重是针对性发挥式树的辨析、和促成和谐的IQueryable<T>、IQueryProvider做了一个笔录以及小结,其中不定有误的下结论或说法,轻点拍!

demo下载:http://pan.baidu.com/s/1nvAksgL 

本文为合至索引目录:《C#基础知识巩固》

 

推荐阅读:

http://www.cnblogs.com/jesse2013/p/expressiontree-part1.html

http://www.cnblogs.com/jesse2013/p/expressiontree-part2.html

http://www.cnblogs.com/jesse2013/p/expressiontree-Linq-to-cnblogs.html

园友@风口上之猪推荐:

http://www.cnblogs.com/Ninputer/archive/2009/09/08/expression_tree3.html
http://blog.zhaojie.me/2009/03/expression-cache-1.html

 

网站地图xml地图