開篇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控件中。
界面以下。
![](http://static.javashuo.com/static/loading.gif)
代碼以下。
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; } }