<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
同步方法:如果一個方法被呼叫了,等待其執行所有處理後呼叫方法才繼續執行的方法。
非同步方法:非同步方法能在處理完成之前就回到呼叫方法。
呼叫方法:該方法呼叫非同步方法,然後在非同步方法(可能在相同的執行緒,也可能在不同的執行緒)執行其任務時繼續執行。
非同步(async)方法:該方法非同步執行其工作,被呼叫時立即回到呼叫方法。
await表示式:用於非同步方法內部,指明需要非同步執行的任務。一個非同步方法需要至少包含一個await表示式。
非同步方法在完成其工作之前即返回到呼叫方法,然後在呼叫方法繼續執行的時候完成其工作。
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(); } }
到此這篇關於C#使用Task實現非同步方法的文章就介紹到這了,更多相關C# Task非同步內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45