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("第二个已经停止");
}

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

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

  1. 异步编程 z

    走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $&qu ...

  2. .Net异步编程 z

    1. 引言 最近在学习Abp框架,发现Abp框架的很多Api都提供了同步异步两种写法.异步编程说起来,大家可能都会说异步编程性能好.但好在哪里,引入了什么问题,以及如何使用,想必也未必能答的上来. 自 ...

  3. ASP.Net Core异步编程

    ASP.Net Core异步编程 概念 什么是异步编程? 异步编程是可以让程序并行运行的一种手段,其可以让程序中的一个工作单元与主应用程序线程分开独立运行,并且在工作单元运行结束后,会通知主应用程序线 ...

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

    你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net F ...

  5. C# 异步编程小结

    APM 异步编程模型,Asynchronous Programming Model EAP 基于事件的异步编程模式,Event-based Asynchronous Pattern TAP 基于任务的 ...

  6. C#~异步编程再续~你必须要知道的ThreadPool里的throw

    问题依旧存在 之前写过相关文章异步编程的文章,本文主要还是一点补充,之前在IIS经常发w3wp进程无做挂了的情况,但一直没能找到真正的原因,而查找相关资料,找了一些相关的文章,如await和async ...

  7. C#异步编程(一)

    异步编程简介 前言 本人学习.Net两年有余,是第一次写博客,虽然写的很认真,当毕竟是第一次,肯定会有很多不足之处, 希望大家照顾照顾新人,有错误之处可以指出来,我会虚心接受的. 何谓异步 与同步相对 ...

  8. C#与C++的发展历程第三 - C#5.0异步编程巅峰

    系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...

  9. 关于如何提高Web服务端并发效率的异步编程技术

    最近我研究技术的一个重点是java的多线程开发,在我早期学习java的时候,很多书上把java的多线程开发标榜为简单易用,这个简单易用是以C语言作为参照的,不过我也没有使用过C语言开发过多线程,我只知 ...

随机推荐

  1. mount挂载错误

    错误信息: usa@usa-desktop:~/svn/aispeech/speechplatform/trunk/proxy/src$ sudo mount -t tmpfs -o size=200 ...

  2. mysql学习之-show table status(获取表的信息)参数说明

    --获取表的信息mysql> show table status like 'columns_priv'\G;*************************** 1. row ******* ...

  3. Oracle 10g bigfile表空间、smallfile 表空间

    smallfile tablespace设置不同大小的db_block_size时数据文件允许的最大大小 db_block_size=2KB,2KB*4M=8192M      8Gdb_block_ ...

  4. linux日志处理logrotate使用

    摘录自:http://linux008.blog.51cto.com/2837805/555829 内容在这里做个备份,以便以后查看:  使用logrotate管理nginx日志文件 2011-04- ...

  5. 写window应用程序日志System.Diagnostics.EventLog.WriteEntry

    System.Diagnostics.EventLog.WriteEntry( MySource , Writing to event log. ); 可以写window应用程序日志 查看的地方:右击 ...

  6. 开放平台-web实现人人网第三方登录

    应用场景     web应用通过人人网登录授权实现第三方登录.   操作步骤     1  注册成为人人网开放平台开发者         http://app.renren.com/developer ...

  7. HTTP响应头和请求头信息对照表

    HTTP请求头提供了关于请求,响应或者其他的发送实体的信息.HTTP的头信息包括通用头.请求头.响应头和实体头四个部分.每个头域由一个域名,冒号(:)和域值三部分组成. 通用头标:即可用于请求,也可用 ...

  8. Eclipse中新建jsp文件访问页面时乱码问题

    新建.jsp文件,charset和pageEncoding默认是ISO-8859-1,这样的话访问页面时会出现乱码,解决办法:将charset和pageEncoding改为UTF-8(或者GBK/GB ...

  9. Neutron Networking QoS

    目前,Neutron有一个QoS的proposal(https://wiki.openstack.org/wiki/Neutron/QoS#Documents),但是只有Ciscso和NVP插件实现了 ...

  10. 磁盘IOPS的计算

    计算磁盘IOPS的三个因素:   1.RAID类型的读写比   不同RAID类型的IOPS计算公式: RAID类型 公式 RAID5.RAID3 Drive IOPS=Read IOPS + 4*Wr ...