C语⾔中task的⽤法,C#教程之中的async和await关键字
使⽤及Task异步调
其实早在 4.5的时候M$就在中引⼊了async和await关键字(VB为Async和Await)来简化异步调⽤的编程模式。我也早就体验过了,现在写⼀篇⽇志来记录⼀下顺便凑⽇志数量(以后⾯试之前可以⽤这个“复习”⼀下)。
(⼀)传统的异步调⽤
在⽐较“古⽼”的C#程序中经常可以看到IAsyncResult、BeginInvoke之类的异步调⽤“踪迹”。先来简单的复习⼀下吧。
假如我们有⼀个⽅法⽣成字符串,⽽⽣成这个字符串需要10秒中的时间:
复制代码 代码如下:
public class WasteTimeObject
{
public string GetSlowString(int begin, int length)
{
StringBuilder sb = new StringBuilder();
for (int i = begin; i < begin + length; i++)
{
sb.Append(WasteTime(i) + " ");
}
return sb.ToString();
}
private string WasteTime(int current)
{
System.Threading.Thread.Sleep(1000);
return current.ToString();
}
}
我们再做⼀个窗⼝,⽤来请求这个⽅法并把字符串显⽰到⽂本框中。使⽤同步调⽤肯定会把UI线程阻塞掉,要想不把UI阻塞掉就要另起⼀个线程了。基本的步骤如下:
创建⼀个异步调⽤的委托:
复制代码 代码如下:
public delegate string GetSlowStringDelegate(int begin, int length);
然后呢,再异步调⽤这个委托:
复制代码 代码如下:
private void button1_Click(object nder, EventArgs e)
{
WasteTimeObject ad = new WasteTimeObject();
GetSlowStringDelegate d = ad.GetSlowString;童话故事会
textBox1.Text = "Requesting string, ";
IAsyncResult ar = d.BeginInvoke(1, 10, TaskComplete, d);
}
这⾥的BeginInvoke会在原来的基础上再附加两个参数:表⽰执⾏完毕后的回调⽅法AsyncCallBack,最后⼀个参数可以是任何对象,以便从回调⽅法中访问它。不过⼀般情况都是传递的委托实例,以便获取调⽤的结果。
当然我们也可以不⽤回调⽅法,这样就只好不断地循环查询是否执⾏完成了。
然后我们就要编写AsyncCallBack这个回调⽅法了,它接受⼀个IAsyncResult类型的对象表⽰异步调⽤的结果:
复制代码 代码如下:
private void TaskComplete(IAsyncResult ar)
情人节还有几天
{
if (ar == null) return;
GetSlowStringDelegate d = ar.AsyncState as GetSlowStringDelegate;
if (d == null) throw new Exception("Invalue object type");
string result = d.EndInvoke(ar);
this.Invoke(new Action(() => UpdateTextResult(result)));
}
调⽤委托实例的EndInvoke⽅法并传⼊IAsyncResult类型的对象⽤以获取GetSlowString的返回结果。
回调⽅法是委托线程调⽤的,因此它不能直接访问UI,所以我们使⽤窗体的Invoke⽅法在主线程中显⽰结果。如果委托⽅法抛出异常,将会在EndInvoke时抛出。
(⼆)使⽤Task类型
可以看到使⽤传统的办法编写异步调⽤很⿇烦,特别是如果这种调⽤很多,那么我们的程序就会变成很复杂,逻辑很乱。
4.5提供的新的异步变成模式就很好地解决了这个问题(其实本质上应该是⾃动实现了很多操作),使编写异步代码和同步调⽤⼀样逻辑清晰。
⾸先来看看微软的例⼦:
复制代码 代码如下:
private async Task SumPageSizesAsync()
图书角
{
// To u the HttpClient type in desktop apps, you must include a using directive and add a
// reference for the System.Net.Http namespace.
HttpClient client = new HttpClient();
// Equivalently, now that you e how it works, you can write the same thing in a single line.
byte[] urlContents = await client.GetByteArrayAsync(url);
// . . .
}
可以看出,使⽤await关键字后,会⾃动把返回结果包装在⼀个Task类型的对象中。对于这个⽰例,⽅法是没有返回结果的。⽽对有返回结果的⽅法,就要使⽤Task了:
复制代码 代码如下:
public async Task WaitAsynchronouslyAsync()
{
await Task.Delay(10000);
return "Finished";
}
总⽽⾔之,使⽤await表达式时,控制会返回到调⽤此⽅法的线程中;在await等待的⽅法执⾏完毕后,控制会⾃动返回到下⾯的语句中。发⽣异常时,异常会在await表达式中抛出。
对于我们这个例⼦,我们编写的代码如下:
复制代码 代码如下:
private async void button1_Click(object nder, EventArgs e)
{
textBox1.Text = "Requesting string, ";
WasteTimeObject ad = new WasteTimeObject();
string result = await Task.Run(() => ad.GetSlowString(1, 10));
//Update UI to display the result
textBox1.Text = result;
}
我们使⽤Task类新建⼀个⼯作线程并执⾏。当然我们也可以像M$给的例⼦那样改造⼀下GetSlowString,这样就不需要加上Task.Run 了。(基本上,这种⽅法都会以Async后缀结尾。)
如何?原来的:创建异步委托→回调⼀⽓呵成。另外还有⼀点,await下⾯的语句是由主线程调⽤的,不是由新的线程调⽤,所以我们可以直接访问UI。
(三)取消执⾏和显⽰进度
最后⼀个要记录的,就是如何给异步调⽤添加进度条,并能让⽤户取消操作。界⾯就是下⾯这样:
使⽤最终完成的代码来说明吧。⾸先改造GetSlowString⽅法,使之⽀持取消和汇报进度:
复制代码 代码如下:
public string GetSlowString(int begin, int length, IProgress progress, CancellationToken cancel)
{
StringBuilder sb = new StringBuilder();
for (int i = begin; i < begin + length; i++)
{
孔子家语sb.Append(WasteTime(i) + " ");
cancel.ThrowIfCancellationRequested();
if (progress != null)
progress.Report((int)((double)(i - begin + 1) * 100 / length));
}
return sb.ToString();
}
IProgress类型的对象有⼀个Report⽅法,执⾏这个⽅法实际上会调⽤⾃定义的更新进度的⽅法,这个⽅法(使⽤委托或匿名⽅法皆可)是在⽣成Progress对象的时候指定的:
复制代码 代码如下:
IProgress progress = new Progress((progressValue) => { progressBar1.Value = progressValue; });
神奇的是,这个⽅法是由主线程调⽤的,如果不是这样,它就不能更新我们界⾯上的控件。所以说微软提供的新机制帮我们简化了很多⼯作。
CancellationToken⽤于指定该⽅法“绑定”的取消上下⽂,如果这个对象执⾏过Cancel⽅法(⽤户点击了Cancel按钮),那么访问ThrowIfCancellationRequested时就会抛出OperationCanceledException类型的异常。这种机制的灵活性在于中⽌执⾏的位置是可以⾃⾏确定的,不会出现取消时⾃⼰都不知道执⾏到哪⾏代码的情况。
总⽽⾔之,单击request按钮的代码我们修改如下:
复制代码 代码如下:
private async void button1_Click(object nder, EventArgs e)
{
cancelSource = new CancellationTokenSource();
笑英语怎么读
IProgress progress = new Progress((progressValue) => { progressBar1.Value = progressValue; });
郑州游玩textBox1.Text = "Requesting string, ";
button1.Enabled = fal; button2.Enabled = true;
WasteTimeObject ad = new WasteTimeObject();
try
{
string result = await Task.Run(() => ad.GetSlowString(1, 10, progress, cancelSource.Token),
cancelSource.Token);
/
/Update UI to display the result
textBox1.Text = result;
button2.Enabled = fal; //Disable cancel button
}
catch (OperationCanceledException)
{
textBox1.Text = "You canceled the operation.";衢州市人民政府
}
}
取消按钮的代码就很简单了:
复制代码 代码如下:
private void button2_Click(object nder, EventArgs e)
{官宣文案
if (cancelSource != null) cancelSource.Cancel();
button2.Enabled = fal;
}
⾄此,Task机制的初步体验就到此完成。以后有机会在研究下更⾼阶的内容吧。