歡迎光臨
每天分享高質量文章

C#語法——await與async的正確打開方式

作者:kiba518

鏈接:https://www.cnblogs.com/kiba/p/9292904.html

原文鏈接:https://stackify.com/net-core-vs-net-framework/

C#5.0推出了新語法,await與async,但相信大家還是很少使用它們。關於await與async有很多文章講解,但有沒有這樣一種感覺,你看完後,總感覺這東西很不錯,但用的時候,總是想不起來,或者不知道該怎麼用。

 

為什麼呢?我覺得大家的await與async的打開方式不正確。

正確的打開方式

首先看下使用約束。

 

1、await 只能在標記了async的函式內使用。

2、await 等待的函式必須標記async。

 

有沒有感覺這是個迴圈?沒錯,這就是個迴圈。這也就是為什麼大家不怎麼用他們的原因。這個迴圈很討厭,那麼怎麼破除這個迴圈呢?

 

【很簡單,await等待的是執行緒,不是函式。】

 

不理解嗎?沒關係,接著看下去。

 

下麵從頭來講解,首先看這麼一組對比

public static int NoAsyncTest()
{
   return 1;
}
public static async Task<intAsyncTest()
{
  return 1;
}

async Task等於int

 

這意味著我們在正常呼叫這兩個函式時,他們是等效的。那麼用async Task來修飾int目的是什麼呢?

 

目的是為了讓這個方法這樣被呼叫 await AsyncTest(),但直接這樣呼叫,並不會開啟執行緒,那這樣費勁的修飾是不是就沒什麼意義了呢。

 

當然不是,那什麼時候會讓 await AsyncTest()有意義呢?

 

我們接著往下看,修改AsyncTest如下。然後,此時再呼叫await AsyncTest(),你會神奇的發現,依然沒有卵用。。。

 

Excute方法正常執行,而AsyncTest內運行的執行緒,自己執行自己的。

public static async void Excute()
 
{
       Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 開始 Excute " + DateTime.Now);
       await AsyncTest();
       Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 結束 Excute " + DateTime.Now);
 }

 public static async Task<intAsyncTest()
 
{
        Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            return 1;
 }

 

 

彆著急,我們稍作調整,在執行緒後面增加.GetAwaiter().GetResult()。這句話是乾什麼用的呢?是用來獲取執行緒傳回值的。

 

這個邏輯是這樣的,如果想要獲取執行緒傳回結果,就自然要等待執行緒結束。

 

運行一下,我們將看下麵的結果。

 

public static async Task<intAsyncTest()
        
{
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }

 

 

但是,好像await AsyncTest();還是沒啟作用。沒錯,事實就是,他真的不會起作用。。。

 

那麼怎麼才能讓他起作用呢?

 

首先,我們定義一個普通函式,他的傳回值是一個Task,然後我們得到Task後,運行它,再用await等待這個Task。

 

於是我們就得到這樣的結果。

 

public static async void Excute()
       
{
           Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 開始 Excute " + DateTime.Now);
           var waitTask = AsyncTestRun();
           waitTask.Start();
           int i = await waitTask;
           Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
           Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 結束 Excute " + DateTime.Now);
       }
       public static Task<intAsyncTestRun()
       
{
           Task<int> t = new Task<int>(() => {
               Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
               Thread.Sleep(1000);
               return 100;
           });
           return t;
       }

  

 

如圖,這樣寫await AsyncTest();就起作用了。

 

所以,還是那句話,await等待的是執行緒,不是函式。

 

但在圖裡,我們發現很奇怪的一點,結束Excute也是執行緒3,而不是執行緒1。也就是說,Await會對執行緒進行優化。

 

下麵看下兩組代碼的對比,讓我們就更清楚的瞭解下Await。

 

第一組,使用await等待執行緒。

 

public static async void Excute()
{
   Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 開始 Excute " + DateTime.Now);
   await SingleAwait();
   Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 結束 Excute " + DateTime.Now);
}

public static async Task SingleAwait()
{
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest開始 " + DateTime.Now);
     await Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            await Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest結束 " + DateTime.Now);
            return;
}

 

 

第二組,使用等待執行緒結果,等待執行緒。

public static async void Excute()
{
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 開始 Excute " + DateTime.Now);
            await SingleNoAwait();
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 結束 Excute " + DateTime.Now);
        }
public static async Task SingleNoAwait()
{
      Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait開始 " + DateTime.Now);
       Task.Run(() =>
        {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait結束 " + DateTime.Now);
            return;
}

 

 

可以明確的看到,第二組,執行緒重新回到了主執行緒1中,而第一組,已經被優化到了執行緒4中。

結語

await是一種很便捷的語法,他的確會讓代碼簡潔一些,但他主動優化執行緒的功能,如果不瞭解就使用,可能會導致一些奇怪的BUG發生。

 

這也是官方為什麼只提供了await呼叫服務的例子,因為,在程式內呼叫,await還是要瞭解後,再使用,才安全。

 

  • C#語法——委托,架構的血液

    https://www.cnblogs.com/kiba/p/9330936.html

  • C#語法——元組型別

    https://www.cnblogs.com/kiba/p/9229176.html

  • C#語法——泛型的多種應用

    https://www.cnblogs.com/kiba/p/9321530.html

赞(0)

分享創造快樂