开篇html
异步编程是程序设计的重点也是难点,还记得在刚开始接触.net的时候,看的是一本c#的Winform实例教程,上面大部分都是教咱们如何使用Winform的控件以及操做数据库的实例,那时候作的基本都是数据库的demo,数据量也不大,程序在执行的时候基本上不会出现阻塞的状况。随着不断的深刻.net,也开始进入的实战,在实际的项目,数据量每每都是比较大,特别是在大量的数据入库以及查询数据并进行计算的时候,程序的UI界面每每卡死在那里,发生了阻塞,这时候就须要对计算时间限制的过程进行异步处理,让UI线程继续相应用户的操做,使得用户体验表现比较友好,同时正确的使用异步编程去处理计算限制的操做和耗时IO操做还能提高的应用程序的吞吐量及性能。因而可知,异步编程的重要性。数据库
class Program { public delegate void DoWork(); static void Main(string[] args) { DoWork d = new DoWork(WorkPro);//no.1 d.BeginInvoke(null, null);//no.2 for (int i = 0; i < 100; i++)//no.3 { Thread.Sleep(10);//主线程须要作的事 } Console.WriteLine("主线程done"); Console.ReadKey(); } public static void WorkPro() { //作一些耗时的工做 Thread.Sleep(2000); Console.WriteLine("异步调用结束"); } }
class Program { public delegate int DoWord(int count); static void Main(string[] args) { DoWord d = new DoWord(WorkPro); IAsyncResult r= d.BeginInvoke(1000,null,null);//no.1 int result= d.EndInvoke(r);//no.2 Console.WriteLine(result); for (int i = 0; i < 100; i++)//no.3 { Thread.Sleep(10);//主线程须要作的事 } Console.WriteLine("主线程done"); Console.ReadKey(); } public static int WorkPro(int count) { int sum = 0; //作一些耗时的工做 for (int i = 0; i < count; i++) { sum += i; } return sum; } }
class Program { public delegate int DoWord(int count); static void Main(string[] args) { DoWord d = new DoWord(WorkPro); IAsyncResult r= d.BeginInvoke(100,CallBack ,d);//no.1 for (int i = 0; i < 100; i++) { Thread.Sleep(10);//主线程须要作的事 } Console.WriteLine("主线程done"); Console.ReadKey(); } public static int WorkPro(int count) { int sum = 0; //作一些耗时的工做 for (int i = 0; i < count; i++) { sum += i; Thread.Sleep(10); } return sum; } public static void CallBack(IAsyncResult r) { DoWord d = (DoWord)r.AsyncState; Console.WriteLine("异步调用完成,返回结果为{0}", d.EndInvoke(r)); } }
.net在System.Threading和System.Threading.Tasks这两个命名空间中提供了Thread,ThreadPool,和Task三个类来处理多线程的问题,其中Thread是创建一个专用线程,ThreadPool是使用线程池中工做线程,而Task类是采用任务的方式,其内部也是使用线程池中的工做线程。本节只讲Tread类和Tasks类的使用以及其优劣。编程
class Program { static void Main(string[] args) { Thread t = new Thread(WorkPro);//no.1 t.IsBackground = true;//no.2 t.Start(1000);//no.3 } public static void WorkPro(object t) { //作一些耗时的工做 int count=(int)t; for (int i = 0; i < count; i++) { Thread.Sleep(2000); } Console.WriteLine("任务处理完成"); } }
二、Task类c#
class Program { static void Main(string[] args) { Task t = new Task((c) => { int count = (int)c; for (int i = 0; i < count; i++) { Thread.Sleep(10); } Console.WriteLine("任务处理完成"); }, 100);//no.1 t.Start(); for (int i = 0; i < 100; i++) { Thread.Sleep(10); } Console.WriteLine("done"); } }
class Program { static void Main(string[] args) { Task<int> t = new Task<int>((c) => { int count = (int)c; int sum=0; for (int i = 0; i < count; i++) { Thread.Sleep(10); sum+=i; } Console.WriteLine("任务处理完成"); return sum; }, 100); t.Start(); t.Wait();//no.1 Console.WriteLine("任务执行的结果{0}", t.Result);//no.2 for (int i = 0; i < 100; i++) { Thread.Sleep(10); } Console.WriteLine("done"); } }
class Program { static void Main(string[] args) { CancellationTokenSource cts = new CancellationTokenSource();//no.1 Task<int> t = new Task<int>((c) =>Sum(cts.Token ,(int)c), 100);//no.2 t.Start(); cts.Cancel();//no.3若是任务还没完成,可是Task有可能完成啦 for (int i = 0; i < 100; i++) { Thread.Sleep(10); } Console.WriteLine("done"); } static int Sum(CancellationToken ct, int count) { int sum = 0; for (int i = 0; i < count; i++) { if (!ct.CanBeCanceled) { Thread.Sleep(10); sum += i; } else { Console.WriteLine("任务取消"); //进行回滚操做 return -1;//退出任务 } } Console.WriteLine("任务处理完成"); return sum; } }
public Task ContinueWith( Action<Task> continuationAction, TaskContinuationOptions continuationOptions )第二个参数表明新任务的执行条件,当任务知足这个枚举条件才执行 Action<Task>类型的回调函数。安全
class Program { static void Main(string[] args) { Task<int> t = new Task<int>((c) =>Sum((int)c), 100); t.Start(); t.ContinueWith(task => Console.WriteLine("任务完成的结果{0}", task.Result));//当任务执行完以后执行 t.ContinueWith(task => Console.WriteLine(""), TaskContinuationOptions.OnlyOnFaulted);//当任务出现异常时才执行 for (int i = 0; i < 200; i++) { Thread.Sleep(10); } Console.WriteLine("done"); } static int Sum( int count) { int sum = 0; for (int i = 0; i < count; i++) { Thread.Sleep(10); sum += i; } Console.WriteLine("任务处理完成"); return sum; } }
t.Start()以后调用第一个ContinueWith方法,该方法第一参数就是一个Action<Task>的委托类型,至关因而一个回调函数,在这里我也用lambda表达式,当任务完成就会启用一个新任务去执行这个回调函数。而第二个ContinueWith里面的回调方法却不会执行,由于咱们的任务也就是Sum方法不会发生异常,不能知足TaskContinuationOptions.OnlyOnFaulted这个枚举条件。这种用法比委托的异步函数编程看起来要简单些。最关键的是ContinueWith的还有一个重载版本能够带一个TaskScheduler对象参数,该对象负责执行被调度的任务。FCL中提供两种任务调度器,均派生自TaskScheduler类型:线程池调度器,和同步上下文任务调用器。而在Winform窗体程序设计中TaskScheduler尤其有用,为何这么说呢?由于在窗体程序中的控件都是有ui线程去建立,而咱们所执行的后台任务使用线程都是线程池中的工做线程,因此当咱们的任务完成以后须要反馈到Winform控件上,可是控件建立的线程和任务执行的线程不是同一个线程,若是在任务线程中去更新控件就会致使控件对象安全问题会出现异常。因此操做控件,就必需要使用ui线程去操做。所以在ContinueWith获取任务执行的结果的并反馈到控件的任务调度上不能使用线程池任务调用器,而要使用同步上下文任务调度器去调度,即采用ui这个线程去调用ContinueWith方法所绑定的回调用函数即Action<Task>类型的委托。下面将使用任务调度器来把异步执行的Sum计算结果反馈到Winform界面的TextBox控件中。
界面以下。

代码以下。
public partial class Form1 : Form { private readonly TaskScheduler contextTaskScheduler;//声明一个任务调度器 public Form1() { InitializeComponent(); contextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();//no.1得到一个上下文任务调度器 } private void button1_Click(object sender, EventArgs e) { Task<int> t = new Task<int>((n) => Sum((int)n),100); t.Start(); t.ContinueWith(task =>this.textBox1 .Text =task.Result.ToString(),contextTaskScheduler);//当任务执行完以后执行 t.ContinueWith(task=>MessageBox .Show ("任务出现异常"),CancellationToken.None ,TaskContinuationOptions.OnlyOnFaulted,contextTaskScheduler );//当任务出现异常时才执行 } int Sum(int count) { int sum = 0; for (int i = 0; i < count; i++) { Thread.Sleep(10); sum += i; } Console.WriteLine("任务处理完成"); return sum; } }