赞
踩
目录
异步方法在完成其工作之前即返回到调用方法,然后在调用方法继续执行的时候完成其工作。
1. Task<T> :如果调用方法要从调用中获取一个T类型的值,异步方法的返回类型就必须是Task<T>。调用方法通过读取Task的Result属性来获取这个T类型的值。
- // 使用返回Task<int>对象的异步方法代码示例:
- static class DoAsyncStuff
- {
- public static async Task<int> CalculateSumAsync(int i1, int i2)
- {
- int sum = await Task.Run(() => GetSum(i1, i2));
- return sum;//注意返回的是一个int类型
- }
-
- private static int GetSum(int i1, int i2) { return i1 + i2; }
-
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- Task<int> task = DoAsyncStuff.CalculateSumAsync(1, 2);
- //处理其他事情...
- Console.WriteLine("Value:{0}",task.Result);//调用方法通过Result获取这个int类型的值
-
- }
- }
2.Task : 如果调用方法不需要从异步方法中返回某个值,但需要检查异步方法的状态,那么异步方法可以返回一个Task类型的对象,这时即使异步方法中出现了return语句,也不会返回任何东西。
- //使用Task不返回类型的异步方法代码示例:
- static class DoAsyncStuff
- {
- public static async Task CalculateSumAsync(int i1, int i2)
- {
- int sum = await Task.Run(() => GetSum(i1, i2));
- Console.WriteLine("Value:{0}",sum);
- }
-
- private static int GetSum(int i1, int i2) { return i1 + i2; }
-
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- Task task = DoAsyncStuff.CalculateSumAsync(1, 2);
- //处理其他事情...
- task.Wait();
- Console.WriteLine("异步方法结束");
- }
- }
3. void :如果调用方法仅仅想执行异步方法,而不需要与它进行进一步交互时【称为“调用并忘记”】,异步方法可以返回void类型。
- //使用“调用并忘记”的异步方法的代码示例:
- static class DoAsyncStuff
- {
- public static async void CalculateSumAsync(int i1, int i2)
- {
- int sum = await Task.Run(() => GetSum(i1, i2));
- Console.WriteLine("Value:{0}",sum);
- }
-
- private static int GetSum(int i1, int i2) { return i1 + i2; }
-
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- DoAsyncStuff.CalculateSumAsync(1, 2);
- //处理其他事情...
- Thread.Sleep(200);
- Console.WriteLine("Program Exiting");
- }
- }
1. 异步方法的结构包含三个不同的区域:
控制流阐述:
需要注意的是:异步方法的return语句并没有真正返回一个值,它只是退出了,异步方法中的返回类型始终是方法头中声明的返回类型。
await表达式指定了一个异步执行的任务。这个任务可能是一个Task类型的对象,也可能不是,默认情况下这个任务在当前线程异步运行。
我们可能需要编写自己的方法作为await表达式的任务。最简单的方式是在你的方法中使用Task.Run创建一个Task。(关于Task.Run即在不同的线程上运行你的方法)Task.Run方法有8个重载 ,如下图所示:
以Func<int> 委托作为参数的代码示例:
- class MyClass
- {
- public int Get10()
- {
- return 10;
- }
-
- public async Task DoWorkAsync()
- {
- //方式1:使用Get10创建名为ten的Func<int>委托,将该委托传入Task.Run方法
- Func<int> ten = new Func<int>(Get10);
- int a = await Task.Run(ten);
-
- //方式2:直接在Task.Run中创建委托,传入Get10方法
- int b = await Task.Run(new Func<int>(Get10));
-
- //方式3:使用与Func<T>兼容的Lambda表达式,Lambda表达式将隐式转换为该委托
- int c = await Task.Run(() => { return 10; });
-
- Console.WriteLine("{0},{1},{2}",a,b,c);
- }
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- Task task = new MyClass().DoWorkAsync();
- task.Wait();
- }
- }
从Task.Run的重载中可以发现,可以作为Run中第一个参数的委托有四种:
使用四种委托作为参数的Run方法代码示例:
- class MyClass
- {
- public static async Task DoWorkAsync()
- {
- await Task.Run(() => Console.WriteLine(5.ToString()));//Aciton,无参无返
-
- Console.WriteLine((await Task.Run(() => 6)).ToString());//TResult Func(),无参有返,返回TResult类型对象
-
- await Task.Run(() => Task.Run(() => Console.WriteLine(7.ToString())));//Task Func(),无参有返,返回简单Task对象
-
- Console.WriteLine((await Task.Run(() => Task.Run(() => 8))).ToString());//Task<TResult> Func(),无参有返,返回Task<T>对象
- }
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- Task task = MyClass.DoWorkAsync();
- task.Wait();
- }
- }
假如我们需要一个接受参数的方法,但是无法匹配上述的四种委托(接受没有参数的方法的委托),我们可以创建一个Lambda表达式,其唯一行为就是运行我们的接受参数的方法,来满足Run方法所能接受的委托形式。代码示例如下:
- class MyClass
- {
- //需要使用异步的方法
- public static int GetSum(int a, int b) { return a + b; }
-
- public static async Task DoWorkAsync(int a,int b)
- {
- int result = await Task.Run(() => GetSum(a, b));//创建一个满足Func<TResult>委托形式的Lambda表达式
- Console.WriteLine(result);
- }
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- Task task = MyClass.DoWorkAsync(6,7);
- task.Wait();
- }
- }
一些.NET异步方法允许你请求终止执行,我们可以在自己的异步方法中加入这个特性。有两个类:CancellationToken和CancellationTokenSource是专门为了取消异步操作设计的。
1. CancellationToken对象包含了一个任务是否应被取消的消息。拥有CancellationToken对象的任务需要定期检查其令牌(token)的状态,如果CancellationToken对象中的IsCancellationRequested属性为true,任务需停止其操作并返回。CancellationToken是不可逆的,也就是一旦IsCancellationRequested设置为true就不能更改了。
2. CancellationTokenSource对象创建可分配给不同任务的CancellationToken对象。任何持有CancellationTokenSource的对象都可以调用其Cancel方法,这会将CancellationToken对象中的IsCancellationRequested设置为true。
3. 使用这两个取消类的代码:
- class MyClass
- {
- public async Task RunAsync(CancellationToken ct)
- {
- if (ct.IsCancellationRequested)
- return;
- await Task.Run(()=>CycleMethod(ct));
- }
-
- void CycleMethod(CancellationToken ct)
- {
- Console.WriteLine("开始CycleMethod方法");
-
- const int max = 5;
-
- for (int i = 0; i < max; i++)
- {
- if (ct.IsCancellationRequested)
- return;
- Thread.Sleep(1000);
- Console.WriteLine("完成:{0}/{1}",i+1,max);
- }
- }
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- CancellationTokenSource cts = new CancellationTokenSource();
- CancellationToken ct = cts.Token;
-
- MyClass mc = new MyClass();
-
- Task t = mc.RunAsync(ct);
-
- /*
- 取消异步操作的过程是协同的:
- 即调用CancellationTokenSource的Cancel时,它本身并不会执行取消操作
- 而是会将CancellationToken的IsCancellationRequested属性设置为true
- 包含CancellationToken的代码负责检查该属性,并判断是否需要停止执行并返回
- */
- //Thread.Sleep(3000);
- //cts.Cancel();//解除注释将产生取消异步的操作
-
- t.Wait();
- Console.WriteLine("是否取消了异步方法:{0}", ct.IsCancellationRequested);
-
- }
- }
就使用try...catch...正常处理异常就行。
- class MyClass
- {
- public static async Task BadAsync()
- {
- try
- {
- await Task.Run(() => { throw new Exception(); });
- }catch (Exception ex)
- {
- Console.WriteLine("异步中抛出异常...");
- }
- }
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- Task task = MyClass.BadAsync();
- task.Wait();
- Console.WriteLine("Task Status:{0}",task.Status);//Task Status:RanToCompletion,因为Task没有被取消
- Console.WriteLine("Task IsFaulted:{0}",task.IsFaulted);//Task IsFaulted:False,因为没有未处理的异常
-
-
- }
- }
Task的Wait方法可以让调用方法等待异步方法完成,在Wait方法处,流程是阻塞的。
- class MyClass
- {
- public void PrintA()
- {
- const int max = 10;
- for (int i = 0; i < max; i++)
- {
- Thread.Sleep(1000);
- Console.WriteLine("A任务完成:{0}/{1}", i + 1, max);
- }
- }
- public async Task ForWaitAsync()
- {
- await Task.Run(new Action(PrintA));
- }
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- MyClass mc = new MyClass();
- Task t = mc.ForWaitAsync();
-
- Thread.Sleep(6000);//主线程休眠6秒
-
- //等待任务完成,使用此方法可以让主方法等待异步方法完成,否则主方法休眠六秒结束时异步方法也将直接结束
- t.Wait();
- }
- }
Wait是等待单一任务完成,我们也可以使用WaitAll和WaitAny等待多个任务。(这两个方法是同步方法,知道等待条件满足后再继续执行,不然流程会阻塞在那里)
- class MyClass
- {
- public void PrintA()
- {
- const int max = 10;
- for (int i = 0; i < max; i++)
- {
- Thread.Sleep(1000);
- Console.WriteLine("A任务完成:{0}/{1}", i + 1, max);
- }
- Console.WriteLine("A任务全部完成!");
- }
-
- public void PrintB()
- {
- const int max = 5;
- for (int i = 0; i < max; i++)
- {
- Thread.Sleep(1000);
- Console.WriteLine("B任务完成:{0}/{1}", i + 1, max);
- }
- Console.WriteLine("B任务全部完成!");
- }
-
- public async Task AForWaitAsync()
- {
- await Task.Run(new Action(PrintA));
- }
-
- public async Task BForWaitAsync()
- {
- await Task.Run(new Action(PrintB));
- }
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- MyClass mc = new MyClass();
- Task a = mc.AForWaitAsync();
- Task b = mc.BForWaitAsync();
-
- Task[] task = new Task[] { a, b };//存放Task的数组
-
- Thread.Sleep(8000);//调用方法休眠8秒
-
- //Task.WaitAll(task);//等待所有任务完成
- Task.WaitAny(task);//等待任意一个任务完成后将不再阻塞
-
- }
- }
WaitAll和WaitAny分别还包含四个重载:
有时在异步方法中,你会希望用await表达式来等待Task。这时异步方法会返回到调用方法,但该异步方法会等待一个或所有任务完成。可以通过Task.WhenAll或Task.WhenAny方法来实现。这两个方法称为组合子。
- class MyClass
- {
- public void PrintA()
- {
- const int max = 10;
- for (int i = 0; i < max; i++)
- {
- Thread.Sleep(1000);
- Console.WriteLine("A任务完成:{0}/{1}", i + 1, max);
- }
- }
-
- public void PrintB()
- {
- const int max = 5;
- for (int i = 0; i < max; i++)
- {
- Thread.Sleep(1000);
- Console.WriteLine("B任务完成:{0}/{1}", i + 1, max);
- }
- }
-
- public async Task AAsync()
- {
- await Task.Run(new Action(PrintA));
- }
-
- public async Task BAsync()
- {
- await Task.Run(new Action(PrintB));
- }
-
- public async Task ForWhenAsync()
- {
- Task a = AAsync();
- Task b = BAsync();
-
- Task[] tasks = { a, b };//Task数组
-
- //await Task.WhenAll(tasks);//在异步中等待所有任务
- await Task.WhenAny(tasks);//在异步中等待任意一个任务
-
- //此异步方法中的流程会阻塞在Task.WhenAny(tasks)中,直到某个条件满足才会到达这里
- Console.WriteLine("现在任务A是否完成:{0}",a.IsCompleted?"Yes":"No");
- Console.WriteLine("现在任务B是否完成:{0}",b.IsCompleted?"Yes":"No");
- }
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- const int max = 12;
-
- MyClass mc = new MyClass();
- mc.ForWhenAsync();
-
- for (int i = 0;i < max ; i++)
- {
- Thread.Sleep(1000);
- Console.WriteLine("Main任务完成:{0}/{1}", i + 1, max);
- }
-
- }
- }
Task.Delay方法创建一个对象,该对象将暂停其在线程中的处理,并在一定时间之后完成。和Thread.Sleep阻塞线程不同的是,Task.Delay方法不会阻塞线程,线程可以继续处理其他工作。
- class Simple
- {
- Stopwatch sw = new Stopwatch();
-
- public void DoRun()
- {
- Console.WriteLine("调用之前");
- ShowDelayAsync();
- Console.WriteLine("调用之后");
- }
-
- private async void ShowDelayAsync()
- {
- sw.Start();
- Console.WriteLine("Delay 执行之前:{0}",sw.ElapsedMilliseconds);
- await Task.Delay(1000);//创建了一个Task对象,该对象将暂停其在线程中的处理,所以在打印下一句话之前,控制回到了调用方法打印调用方法中的"调用之后"
- Console.WriteLine("Delay 执行之后:{0}", sw.ElapsedMilliseconds);
- }
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- Simple simple = new Simple();
- simple.DoRun();
- Console.Read();
-
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。