本文主要介紹了C#中 Thread,Task,Async/Await,IAsyncResult的相關知識。具有一定的參考價值,下面跟著小編一起來看下吧
說起異步,Thread,Task,async/await,IAsyncResult 這些東西肯定是繞不開的,今天就來依次聊聊他們
1.執行緒(Thread)
多執行緒的意義在於一個應用程式中,有多個執行部分可以同時執行;對於比較耗時的操作(例如io,資料庫操作),或等待回應(如WCF通訊)的操作,可以單獨開啟後台執行緒來執行,這樣主執行緒就不會阻塞,可以繼續往下執行;等到後台執行緒執行完畢,再通知主執行緒,然後做出對應操作!中開啟新執行緒比較簡單
static void Main(string[] args) { Console.WriteLine("主线程开始"); //IsBackground=true,将其设置为后台线程 Thread t = new Thread(Run) { IsBackground = true }; t.Start(); Console.WriteLine("主线程在做其他的事!"); //主线程结束,后台线程会自动结束,不管有没有执行完成 //Thread.Sleep(300); Thread.Sleep(1500); Console.WriteLine("主线程结束"); } static void Run() { Thread.Sleep(700); Console.WriteLine("这是后台线程调用"); }
執行結果如下圖,
#可以看到在啟動後台執行緒之後,主執行緒繼續往下執行了,並沒有等到後台執行緒執行完之後。對於HTTP請求的處理,那是不是要對每一個請求創建一個後台線程呢?為了解決這個問題,把創建的線程存起來,形成一個線程池(裡面有多個線程),當要處理任務時,若線程池中有空閒線程(前一個任務執行完成後,線程不會被回收,會被設定為空閒狀態),則直接呼叫執行緒池中的執行緒執行(例
asp.net處理機制中的Application
物件),使用例子:for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(m =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());
});
}
Console.Read();
執行結果:
這裡對SemaphoreSlim類別的用法做一個簡單的例子:
static SemaphoreSlim semLim = new SemaphoreSlim(3); //3表示最多只能有三个线程同时访问 static void Main(string[] args) { for (int i = 0; i < 10; i++) { new Thread(SemaphoreTest).Start(); } Console.Read(); } static void SemaphoreTest() { semLim.Wait(); Console.WriteLine("线程" + Thread.CurrentThread.ManagedThreadId.ToString() + "开始执行"); Thread.Sleep(2000); Console.WriteLine("线程" + Thread.CurrentThread.ManagedThreadId.ToString() + "执行完毕"); semLim.Release(); }
執行結果如下:
#可以看到,剛開始只有三個執行緒在執行,當一個線程執行完畢並釋放之後,才會有新的線程來執行方法!做示範了!
2.TaskTask是.NET4.0加入的,跟執行緒池ThreadPool的功能類似,用Task開啟新任務時,會從執行緒池中呼叫線程,而Thread每次實例化都會創建一個新的線程。
Console.WriteLine("主线程启动"); //Task.Run启动一个线程 //Task启动的是后台线程,要在主线程中等待后台线程执行完毕,可以调用Wait方法 //Task task = Task.Factory.StartNew(() => { Thread.Sleep(1500); Console.WriteLine("task启动"); }); Task task = Task.Run(() => { Thread.Sleep(1500); Console.WriteLine("task启动"); }); Thread.Sleep(300); task.Wait(); Console.WriteLine("主线程结束");
執行結果如下:
開啟新任務的方法:Task.Run()或Task.Factory.StartNew(),開啟的是後台執行緒要在主執行緒等待後台執行緒執行完畢,可以使用Wait方法(會以同步的方式來執行)。不用Wait則會以非同步的方式來執行。比較一下Task和Thread:
static void Main(string[] args) { for (int i = 0; i < 5; i++) { new Thread(Run1).Start(); } for (int i = 0; i < 5; i++) { Task.Run(() => { Run2(); }); } } static void Run1() { Console.WriteLine("Thread Id =" + Thread.CurrentThread.ManagedThreadId); } static void Run2() { Console.WriteLine("Task调用的Thread Id =" + Thread.CurrentThread.ManagedThreadId); }執行結果:
#可以看出來,直接用Thread會開啟5個線程,用Task(用了線程池)開啟了3個!
2.1 Taskb54c2c292509147c0b54128f4eb90887 #Taskb54c2c292509147c0b54128f4eb90887就是有回傳值的Task,TResult就是回傳值類型。Console.WriteLine("主线程开始"); //返回值类型为string Task<string> task = Task<string>.Run(() => { Thread.Sleep(2000); return Thread.CurrentThread.ManagedThreadId.ToString(); }); //会等到task执行完毕才会输出; Console.WriteLine(task.Result); Console.WriteLine("主线程结束");運行結果: 透過task.Result可以取到回傳值,若取值的時候,後台執行緒還沒執行完,則會等待其執行完畢!
簡單提一下:Task任務可以透過CancellationTokenSource類別來取消,感覺用得不多,用法比較簡單,感興趣的話可以搜一下!
3. async/await
async/await是C#5.0中推出的,先上用法:static void Main(string[] args) { Console.WriteLine("-------主线程启动-------"); Task<int> task = GetStrLengthAsync(); Console.WriteLine("主线程继续执行"); Console.WriteLine("Task返回的值" + task.Result); Console.WriteLine("-------主线程结束-------"); } static async Task<int> GetStrLengthAsync() { Console.WriteLine("GetStrLengthAsync方法开始执行"); //此处返回的<string>中的字符串类型,而不是Task<string> string str = await GetString(); Console.WriteLine("GetStrLengthAsync方法执行结束"); return str.Length; } static Task<string> GetString() { //Console.WriteLine("GetString方法开始执行") return Task<string>.Run(() => { Thread.Sleep(2000); return "GetString的返回值"; }); }async用來修飾方法,表示這個方法是異步的,聲明的方法的回傳類型必須為:void,Task或Taskb54c2c292509147c0b54128f4eb90887。
await必须用来修饰Task或Taskb54c2c292509147c0b54128f4eb90887,而且只能出现在已经用async关键字修饰的异步方法中。通常情况下,async/await成对出现才有意义,
看看运行结果:
可以看出来,main函数调用GetStrLengthAsync方法后,在await之前,都是同步执行的,直到遇到await关键字,main函数才返回继续执行。
那么是否是在遇到await关键字的时候程序自动开启了一个后台线程去执行GetString方法呢?
现在把GetString方法中的那行注释加上,运行的结果是:
大家可以看到,在遇到await关键字后,没有继续执行GetStrLengthAsync方法后面的操作,也没有马上反回到main函数中,而是执行了GetString的第一行,以此可以判断await这里并没有开启新的线程去执行GetString方法,而是以同步的方式让GetString方法执行,等到执行到GetString方法中的Task98c455a79ddfebb79781bff588e7b37e.Run()的时候才由Task开启了后台线程!
那么await的作用是什么呢?
可以从字面上理解,上面提到task.wait可以让主线程等待后台线程执行完毕,await和wait类似,同样是等待,等待Task98c455a79ddfebb79781bff588e7b37e.Run()开始的后台线程执行完毕,不同的是await不会阻塞主线程,只会让GetStrLengthAsync方法暂停执行。
那么await是怎么做到的呢?有没有开启新线程去等待?
只有两个线程(主线程和Task开启的线程)!至于怎么做到的(我也不知道......>_03065e15d4ebf3426e4f9cf8ae830fea:
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 6, 7, 8, 9 }; Parallel.ForEach<int>(list, n => { Console.WriteLine(n); Thread.Sleep(1000); });
Action[] actions = new Action[] { new Action(()=>{ Console.WriteLine("方法1"); }), new Action(()=>{ Console.WriteLine("方法2"); }) }; Parallel.Invoke(actions);
以上是C#中Thread,Task,Async/Await,IAsyncResult的圖文程式碼詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!