
在使用多线程编写端口扫描程序时,我自己感觉同步和确认所有线程都执行完的时间是2个比较帮忙的难题。有园友评论说今天将要不自动建立thread对象了,而是直接使用Task异步模式,我的网络编程老师也提到了异步编程的优越性。在学习了教材上的常识后,进行了一个总结分享给大家。从.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关键字显示建立任务,但是这种方法用的不多。
TaskRun(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=newCancellationTokenSource();,如果期望在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属性来获得任务执行的状况c 异步编程,当任务完成后还可以借助任务属性了解任务完成的状况。可借助任务案例的Status属性获得任务执行的状况,任务执行的状态用TaskStatus枚举表示,以下是TaskStatus的枚举值:
Created:任务将要初始化,但未能转入调度计划
WaitingForActivation:该任务已处于调度计划,正在等待被调度程序激活
WaitingToRun:该任务已被调度程序激活,但尚未开始执行
Running:该任务正在运行,但未能完成

RanToCompletion:该任务将要失败完成
Canceled:该任务由于被更改而完成,引发异常或调用方已向该任务的CancellationToken发出讯号
Faulted:该任务因为发生未经处置的异常而完成
WaitingForChildrenToComplete:该任务本身已完成,正等待附加的子任务完成
任务完成状况相关的属性有IsCompleted、IsCanceled和IsFaulted等属性,从词汇含义上看不难理解它们的意思,其中要注意IsCompleted属性表示任务能否完成,无论是正常结束还是因为更改或异常而完成都为完成。
5. 任务执行的进度
有时候我们期望让个别异步操作提供进度通知,以便在图标中显示异步操作执行的进度,可以用Progress<T>类来受到任务执行的进度。以下是运用技巧里的Report方法将步骤内函数的值传回创建任务的事件代码里,从而升级进度条的值。
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/ruanjian/article-120301-1.html
吹牛逼