C#异步编程 z

http://www.cnblogs.com/fangyz/p/5134018.html

从.NET4.5开始,用async和await关键字再加上Task.Run是一个非常不错的异步编程模型。

1.await和async

  异步模式从技术上看就是利用委托来实现的,它 的主要好处是在异步执行的过程中,用户仍然可以操控UI界面。使用Task类和使用Thread类有很多相似的地方,Task类也是通过调用方法去实现一 个任务的完成,方法可是是命名方法或匿名方法,在执行过程中可使用async和await来实现异步执行。async是一个修饰符,它只能用在方法或者事 件处理程序的签名中。对于方法可分为有返回值和无返回值两种情况,事件则只有一种,如下面三条语句所示:

    private async Task<int> MethodAsync();//有返回值的异步方法

    private async Task MethodAsync();//无返回值的异步方法

    private async void btnOk_Click();//异步事件处理程序

  await是一个运算符,它表示等待异步执行 的结果。也可以理解为await运算符实际上是对方法的返回值进行操作,也就是对Task<Result>进行操作,而不是对方法本身进行操 作。还有一点要注意,await是一定要放在异步方法的内部,如果没有放在内部的话,VS会自动报错。以下是async和await使用的例子: 

    private async void button5_Click(object sender, EventArgs e)
    {
      Task a = Method1Async();
      //此处可继续执行其他代码
      await a;//等待任务a完成
      Task<int> b = Method2Async();
      //此处可继续执行其他代码
      int c = await b;//等待任务b完成,且可以拿到任务b的返回值
    }

    Task Method1Async();
    async Task<int> Method2Async()
    {
      await Task.Delay(100);
      return 1;
    }

  await和同步编程最大的不同之处是:异步等待任务完成的时候,在不会继续执行后面的代码时,也不会影响界面的操作。在.NET提供的类中,异步方法都是约定用Async作为后缀,这样可以很清楚的知道这个方法是异步方法还是同步方法。

2. 创建任务

  创建任务也就是将任务与要执行的方法联系起
来,编写任务执行的方法时,这个方法既可以是同步方法也可以是异步方法,还可以是匿名方法。执行异步方法时,必须用async和Task共同表示没有返回
值的任务,用async和Task<TResult>共同表示返回值为TResult的任务。以下是定义执行任务的方法。

    private async void button5_Click(object sender, EventArgs e)
    {

      //Task.Run方法表示使用默认的任务调度程序在线程池中通过后台执行指定的任务

      //如果不需要自己去调度方法,使用这个方式最方便
      await Task.Run(()=>Method1Async());//执行同步方法
      int c = await Task.Run(()=>Method2Async());//执行异步方法
      await Task.Run(async () => { c = 2; });//执行异步匿名方法
    }
    void Method1Async();
    async Task<int> Method2Async(){...}

  Task.Run方法常用的重载形式有以下4种,另外它也是可以用new关键字显示创建任务,但是这种方式用的不多。

    Task Run(Func<Task> function);//执行不带返回值的任务

    Task<TResult> Run<TResult>(Func<Task<TResult>> function);//执行带返回值的任务

    
Task<TResult> Run<TResult>(Func<Task<TResult>>
function, CancellationToken cancellationToken);//执行过程中可以监听取消通知

    Task Run(Func<Task> function, CancellationToken cancellationToken);//执行过程中可以监听取消通知

3. 终止任务

  在执行任务时肯定会出现需要终止任务的情况,
这里的终止告诉任务你要尽快停下来不再执行了,而不是直接销毁任务实例。这里可以打个比方,学生一起出去吃饭了,学生与老师都在班群里面,突然班群里老师
说要让同学们集合,如果所有同学都看到这个消息,然后学生们开始出发,这样就可以正确的集合。上面的例子有一个很重要的前提,那就是所有同学都要看到这个
消息,也就是学生是时刻监听消息的。CancellationTokenSource类和CancellationToken结构用于实现多线程、线程池
和Task任务的取消操作,处理模式与上面的例子相似。创建的班群就是CancellationTokenSource对象,收到的通知就是
CancellationToken对象。CancellationTokenSource用于创建取消通知,CancellationToken则用于
传播应取消操作的通知,当调用任务前,可以先创建取消源对象CancellationTokenSource
cts=new CancellationTokenSource();,如果希望在30秒后自动发出取消通知,可以传入参数
CancellationTokenSource(TimeSpan.FromSeconds(30));CancellationToken
ct=cts.Token;,后一句代码是拿到取消的通知。CancellationTokenSource还有一个Cancel方法,将这个属性设为
true时,该方法会将所有添加了取消标记的CancellationToken对象的IsCancellationRequested属性都设置为
true,这样取消通知就传递到了正在执行的任务。

  任务收到取消通知后,可以选择两种方式来终止
操作。第一种方式是简单的从委托返回。这种实现方式类似于在调用任务的代码中一个bool值来表示取消通知,任务收到后就直接返回了。当采用这种方式时任
务状态的返回值为TaskStatus.RanToCompletion枚举值,它表示正常完成,而不是TaskStatus.Canceled枚举值。
第二种方式是在代码里引发OperationCanceledException异常,并将其传递到在其上请求了取消的标记,采用这种方式取消的任务会转
换为用Canceled枚举值表示的状态。完成引发异常的首选方式是调用ct.ThrowIfCancellationRequestes();。以下是
代码示例,写了一个winform程序,利用进度条来取消任务。第一个图是没有引发异常时,程序退出for循环,执行后面的代码后返回了,第二张图是第二
种方式,引发了异常后直接跳转到catch语句块了。

     CancellationTokenSource cts;
        private async void button3_Click(object sender, EventArgs e)
        {
            progressBar1.Maximum = 100;
            progressBar1.Value = 0;
            cts = new CancellationTokenSource();
            var aa = MYThreadAsync("a", cts.Token);
            try
            {
                await aa;
                listBox1.Items.Add("await后面");
            }
            catch
            {
                if (aa.IsCanceled)
                    listBox1.Items.Add("a取消");
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            cts.Cancel();
        }

        public async Task MYThreadAsync(string s, CancellationToken ct)
        {
            for (int i = 0; i < 50; i++)
            {
                if (ct.IsCancellationRequested)
                    break;          //点击关闭按钮,IsCancellationRequested就为true,就会退出for循环,这是第一种方式
                progressBar1.Value += 2;
                await Task.Delay(100);
                ct.ThrowIfCancellationRequested();//这是第二种方式,它会终止任务并且返回catch语句块里面
            }
            listBox1.Items.Add("任务" + s + "完成了");
        }

4. 获取任务执行的状态

  在异步编程中,很显然任务执行的状态是一个非 常重要的参数。在任务的生命周期里,可以通过Status属性来获取任务执行的状态,当任务完成后还可以通过任务属性知道任务完成的情况。可利用任务实例 的Status属性获取任务执行的状态,任务执行的状态用TaskStatus枚举表示,以下是TaskStatus的枚举值:

Created:任务已经初始化,但尚未进入调度计划

WaitingForActivation:该任务已进入调度计划,正在等待被调度程序激活

WaitingToRun:该任务已被调度程序激活,但尚未开始执行

Running:该任务正在运行,但尚未完成

RanToCompletion:该任务已经成功完成

Canceled:该任务由于被取消而完成,引发异常或调用方已向该任务的CancellationToken发出信号

Faulted:该任务因为出现未经处理的异常而完成

WaitingForChildrenToComplete:该任务本身已完成,正等待附加的子任务完成

  任务完成情况相关的属性有IsCompleted、IsCanceled和IsFaulted等属性,从单词意思上看不难理解它们的意思,其中要注意IsCompleted属性表示任务是否完成,无论是正常结束还是因为取消或异常而完成都为完成。

5. 任务执行的进度

  有时候我们希望让某些异步操作提供进度通知,以便在界面中显示异步操作执行的进度,可以用Progress<T>类来得到任务执行的进度。以下是利用方法里的Report方法将方法内变量的值传回创建任务的事件代码里,从而更新进度条的值。

     CancellationTokenSource cts;
        private async void button3_Click(object sender, EventArgs e)
        {
            progressBar1.Maximum = 100;
            progressBar1.Value = 0;
            cts = new CancellationTokenSource();
            CancellationToken ct = cts.Token;
            var pp = new Progress<int>();
            pp.ProgressChanged += (s, n) => {
                progressBar1.Value = n;
            };
            var tt = Task.Run(()=>MYThreadAsync(pp,cts.Token,500),cts.Token);
            try
            {
                await tt;
                if (tt.Exception == null)
                    listBox1.Items.Add("任务完成");
            }
            catch (Exception ex)
            {
                listBox1.Items.Add("异常" + ex.Message);
            }
        }
        private void button4_Click(object sender, EventArgs e)
        {
            cts.Cancel();
        }
        public  void MYThreadAsync(IProgress<int> progress, CancellationToken ct, int delay)
        {
            int p = 0;//进度
            while (p < 100 && ct.IsCancellationRequested == false)
            {
                p += 1;
                Thread.Sleep(delay);
                progress.Report(p);//这个方法将会触发ProgressChanged事件更新进度条
            }
        }

6. 定时完成任务

  无论是服务器还是客户端,都是有定时完成某个 任务的需要的。System.Timers.Timer类是一个不错的定时设置类,这个类可以引发事件,但它默认是在线程池中引发事件,而不是在当前线程 中引发事件。Timer类的常用属性有AutoReset和Interval属性,AutoReset是获取或设置一个bool值,该值为true表示每 次间隔结束时都引发一次Elapsed事件,false表示仅在首次间隔结束时引发一次该事件。Interval属性是获取或设置两次Elapsed事件 的间隔时间,该值必须大于零并小于Int.MaxValue,默认值为100毫秒。Timer类还有两个常用方法那就是Start和Stop方法。

  还有一个 System.Threading.Timer类,它也是在线程池中定时执行任务,它与前一个Timer类的区别是该类不使用事件模型,而是直接通过 TimerCallback类型的委托来实现的。该类的构造函数为:Timer(TimerCallback callback,Object state,TimeSpan douTime,TimeSpan period)。callback表示要执行的方法,state表示一个包含回调方法要使用的信息的对象,dueTime是首次调用回调方法之前延迟的时 间,period表示每次调用回调方法的时间间隔,-1表示终止。这样创建对象后,首次到达dueTime延时时间会自动调用一次callback委托, 以后每隔period时间间隔调用一次。以下是这两种方式的运行效果和源代码。

     System.Timers.Timer timer;
        System.Threading.Timer threadtimer;
        private void button2_Click(object sender, EventArgs e)//Timers.Timer
        {
            progressBar1.Maximum = 100;
            progressBar1.Value = 0;
            int pro=0;
            timer = new System.Timers.Timer(500);
            timer.AutoReset = true;
            timer.Elapsed+= (obj, args) =>
            {
                pro+=5;
                progressBar1.Value = pro;
            };
            timer.Start();
        }

       private void button5_Click(object sender, EventArgs e)
       {
           timer.Stop();
           listBox1.Items.Add("第一个已经停止");
       }
    //Threading.Timer类
       private void button1_Click(object sender, EventArgs e)
       {
           progressBar2.Maximum = 100;
           progressBar2.Value = 0;
           TimeSpan dueTime = new TimeSpan(0, 0, 0, 1);
           TimeSpan period = new TimeSpan(0, 0, 0, 0, 200);
           System.Threading.TimerCallback timecall = new TimerCallback((obj) => progressBar2.Value += 5);
           threadtimer = new System.Threading.Timer(timecall, null, dueTime, period);
       }

       private void button6_Click(object sender, EventArgs e)
       {
           threadtimer.Dispose();
           listBox1.Items.Add("第二个已经停止");
       }

  这篇文章只总结了单个任务的异步执行的基础,还得继续学习多任务并行执行。如果有更好的技术或者与企业使用相关的异步技术,希望园友可以提出我继续学习。

时间: 2024-10-23 07:14:59

C#异步编程 z的相关文章

异步编程 z

走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $"" 来拼接字符串,相当于string.Format() 方法. 目录 What's 异步? async/await 结构 What’s 异步方法? 一.What's 异步? 启动程序时,系统会在内存中创建一个新的进程.进程是构成运行程序资源的集合. 在进程内部,有称为线程的内核对象,它代表的是

使用Async和Await进行异步编程(C#版 适用于VS2015) z

你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net Framework 4.5和Windows 运行时提供了异步支持.编译器做了开发者以前做的困难的工作,而且你的应用保持了类似于异步代码的逻辑结构.结果,你轻易地就获得了所有异步编程的优势. 异步提升响应 异步对于可能阻塞的活动是至关重要的.例如当你的应用访问Web的时候,访问web资源有时有点慢或者延

JS 异步编程六种方案

前言我们知道Javascript语言的执行环境是"单线程".也就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务. 这种模式虽然实现起来比较简单,执行环境相对单纯,但是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行.常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行. 为了解决这个问题,Javascript语言将任务的执行模式分成两种

Python Twisted网络编程框架与异步编程入门教程

原作出处:twisted-intro-cn 作者:Dave 译者:杨晓伟 luocheng likebeta 转载声明:版权归原作出处所有,转载只为让更多人看到这部优秀作品合集,如果侵权,请留言告知 感 谢:感谢 杨晓伟 luocheng likebeta 为国内Twisted Coder做的里程碑级贡献 其 它:能访问到Github的童鞋,请访问出处链接.因为出处排版相当棒! 1.Twisted理论基础 2.异步编程初探与reactor模式 3.初次认识Twisted 4.由twisted支持

利用 Python yield 创建协程将异步编程同步化

在 Lua 和 Python 等脚本语言中,经常提到一个概念: 协程.也经常会有同学对协程的概念及其作用比较疑惑,本文今天就来探讨下协程的前世今生. 首先回答一个大家最关心的问题:协程的好处是什么? 通俗易懂的回答: 让原来要使用 异步 + 回调 方式写的非人类代码,可以用看似同步的方式写出来. 1.回顾同步与异步编程 同步编程即线性化编程,代码按照既定顺序执行,上一条语句执行完才会执行下一条,否则就一直等在那里. 但是许多实际操作都是CPU 密集型任务和 IO 密集型任务,比如网络请求,此时不

探索Javascript 异步编程

在我们日常编码中,需要异步的场景很多,比如读取文件内容.获取远程数据.发送数据到服务端等.因为浏览器环境里Javascript是单线程的,所以异步编程在前端领域尤为重要. 异步的概念 所谓异步,是指当一个过程调用发出后,调用者不能立刻得到结果.实际处理这个调用的过程在完成后,通过状态.通知或者回调来通知调用者. 比如我们写这篇文字时点击发布按钮,我们并不能马上得到文章发布成功或者失败.等待服务器处理,这段时间我们可以做其他的事情,当服务器处理完成后,通知我们是否发布成功. 所谓同步,是指当一个过

JavaScript异步编程

在一年前初学js的时候,看过很多关于异步编程的讲解.但是由于实践经验少,没有办法理解的太多,太理论的东西也往往是看完就忘. 经过公司的三两个项目的锻炼,终于对js异步编程有了比较具体的理解.但始终入门较浅,在这里就当是给自己一个阶段性的总结. 在异步编程中,一条语句的执行不能依赖上一条语句执行完毕的结果,因为无法预测一条语句什么时候执行完毕,它与代码顺序无关,语句是并发执行的. 例如以下代码:     1 $.get($C.apiPath+'ucenter/padCharge/findMembe

【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单

一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就具体看看编译器到底在背后帮我们做了哪些复杂的工作的. 二.同步代码存在的问题 对于同步的代码,大家肯定都不陌生,因为我们平常写的代码大部分都是同步的,然而同步代码却存在一个很严重的问题,例如我们向一个Web服务器发出一个

让我们再为C#异步编程Async正名

本文版权归博客园和作者吴双本人共同所有.转载和爬虫必须在显要位置注明出处:http://www.cnblogs.com/tdws 半年前翻译了一系列很糟糕的异步编程文章,用异步的常用语来说:"在将来的某个时间" 我还会重新翻译Async in C#5.0 http://www.cnblogs.com/tdws/p/5617242.html 写在前面 异步编程在处理并发方面被使用的越来越多,之所以说上面一句话,是为了区分多线程编程.各位司机都知道,实际上异步编程的核心目标正并发处理.可还