<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文說通C#中的異步編程

          共 15044字,需瀏覽 31分鐘

           ·

          2020-07-28 17:17





          天天寫,不一定就明白。

          又及,前兩天看了一個關(guān)于同步方法中調(diào)用異步方法的文章,里面有些概念不太正確,所以整理了這個文章。

          ?

          一、同步和異步。

          先說同步。

          同步概念大家都很熟悉。在異步概念出來之前,我們的代碼都是按同步的方式寫的。簡單來說,就是程序嚴(yán)格按照代碼的邏輯次序,一行一行執(zhí)行。

          看一段代碼:

          public static void Main(string[] args)
          {
              Console.WriteLine("Syc proccess - start");

              Console.WriteLine("Syc proccess - enter Func1");
              func1();
              Console.WriteLine("Syc proccess - out Func1");

              Console.WriteLine("Syc proccess - enter Func2");
              func2();
              Console.WriteLine("Syc proccess - out Func2");

              Console.WriteLine("Syc proccess - enter Func3");
              func3();
              Console.WriteLine("Syc proccess - out Func3");

              Console.WriteLine("Syc proccess - done");
          }

          private static void func1()
          {
              Console.WriteLine("Func1 proccess - start");
              Thread.Sleep(1000);
              Console.WriteLine("Func1 proccess - end");
          }

          private static void func2()
          {
              Console.WriteLine("Func2 proccess - start");
              Thread.Sleep(3000);
              Console.WriteLine("Func2 proccess - end");
          }

          private static void func3()
          {
              Console.WriteLine("Func3 proccess - start");
              Thread.Sleep(5000);
              Console.WriteLine("Func3 proccess - end");
          }

          這是一段簡單的通常意義上的代碼,程序按代碼的次序同步執(zhí)行,看結(jié)果:

          Syc proccess - start
          Syc proccess - enter Func1
          Func1 proccess - start
          Func1 proccess - end
          Syc proccess - out Func1
          Syc proccess - enter Func2
          Func2 proccess - start
          Func2 proccess - end
          Syc proccess - out Func2
          Syc proccess - enter Func3
          Func3 proccess - start
          Func3 proccess - end
          Syc proccess - out Func3
          Syc proccess - done

          沒有任何意外。

          ?

          那異步呢?

          異步,來自于對同步處理的改良和優(yōu)化。

          應(yīng)用中,經(jīng)常會有對于文件或網(wǎng)絡(luò)、數(shù)據(jù)庫的IO操作。這些操作因?yàn)镮O軟硬件的原因,需要消耗很多時間,但通常情況下CPU計(jì)算量并不大。在同步的代碼中,這個過程會被阻塞。直白的說法就是這一行代碼沒執(zhí)行完成,程序就得等著,等完成后再執(zhí)行下一行代碼,而這個等待的時間中,CPU資源就被浪費(fèi)了,閑著了,什么也沒做。(當(dāng)然,操作系統(tǒng)會調(diào)度CPU干別的,這兒不抬杠。)

          異步編程模型和規(guī)范因此出現(xiàn)了,通過某種機(jī)制,讓程序在等著IO的過程中,繼續(xù)做點(diǎn)別的事,等IO的過程完成了,再回來處理IO的內(nèi)容。這樣CPU也沒閑著,在等IO的過程中多做了點(diǎn)事。反映到用戶端,就感覺程序更快了,用時更短了。

          ?

          下面重點(diǎn)說一下異步編程相關(guān)的內(nèi)容。

          二、異步編程

          C#中,異步編程,一個核心,兩個關(guān)鍵字。

          一個核心是指TaskTask 對象,而兩個關(guān)鍵字,就是asyncawait。

          從各種渠道給出的異步編程,都是下面的方式:

          async Task function()
          {
            /* your code here */
          }

          然后調(diào)用的方式:

          await function();

          是這樣的嗎?嗯,圖樣圖森破~~~

          ?

          我們來看代碼:

          static async Task Main(string[] args)
          {
              Console.WriteLine("Async proccess - start");

              Console.WriteLine("Async proccess - enter Func1");
              await func1();
              Console.WriteLine("Async proccess - out Func1");

              Console.WriteLine("Async proccess - enter Func2");
              await func2();
              Console.WriteLine("Async proccess - out Func2");

              Console.WriteLine("Async proccess - enter Func3");
              await func3();
              Console.WriteLine("Async proccess - out Func3");

              Console.WriteLine("Async proccess - done");

              Console.WriteLine("Main proccess - done");
          }

          private static async Task func1()
          {
              Console.WriteLine("Func1 proccess - start");
              Thread.Sleep(1000);
              Console.WriteLine("Func1 proccess - end");
          }

          private static async Task func2()
          {
              Console.WriteLine("Func2 proccess - start");
              Thread.Sleep(3000);
              Console.WriteLine("Func2 proccess - end");
          }

          private static async Task func3()
          {
              Console.WriteLine("Func3 proccess - start");
              Thread.Sleep(5000);
              Console.WriteLine("Func3 proccess - end");
          }

          跑一下結(jié)果:

          Async proccess - start
          Async proccess - enter Func1
          Func1 proccess - start
          Func1 proccess - end
          Async proccess - out Func1
          Async proccess - enter Func2
          Func2 proccess - start
          Func2 proccess - end
          Async proccess - out Func2
          Async proccess - enter Func3
          Func3 proccess - start
          Func3 proccess - end
          Async proccess - out Func3
          Async proccess - done
          Main proccess - done

          咦?這個好像跟同步代碼的執(zhí)行結(jié)果沒什么區(qū)別???

          ?

          嗯,完全正確。上面這個代碼,真的是同步執(zhí)行的。

          這是異步編程的第一個容易錯誤的理解:asyncawait的配對。

          三、async和await的配對

          在異步編程的規(guī)范中,async修飾的方法,僅僅表示這個方法在內(nèi)部有可能采用異步的方式執(zhí)行,CPU在執(zhí)行這個方法時,會放到一個新的線程中執(zhí)行。

          那這個方法,最終是否采用異步執(zhí)行,不決定于是否用await方式調(diào)用這個方法,而決定于這個方法內(nèi)部,是否有await方式的調(diào)用。

          看代碼,很容易理解:

          private static async Task func1()
          {
              Console.WriteLine("Func1 proccess - start");
              Thread.Sleep(1000);
              Console.WriteLine("Func1 proccess - end");
          }

          這個方法,因?yàn)榉椒▋?nèi)部沒有await調(diào)用,所以這個方法永遠(yuǎn)會以同步方式執(zhí)行,不管你調(diào)用這個方法時,有沒有await。

          而下面這個代碼:

          private static async Task func1()
          {
              Console.WriteLine("Func1 proccess - start");
              await Task.Run(() => Thread.Sleep(1000));
              Console.WriteLine("Func1 proccess - end");
          }

          因?yàn)檫@個方法里有await調(diào)用,所以這個方法不管你以什么方式調(diào)用,有沒有await,都是異步執(zhí)行的。

          看代碼:

          static async Task Main(string[] args)
          {
              Console.WriteLine("Async proccess - start");

              Console.WriteLine("Async proccess - enter Func1");
              func1();
              Console.WriteLine("Async proccess - out Func1");

              Console.WriteLine("Async proccess - enter Func2");
              func2();
              Console.WriteLine("Async proccess - out Func2");

              Console.WriteLine("Async proccess - enter Func3");
              func3();
              Console.WriteLine("Async proccess - out Func3");

              Console.WriteLine("Async proccess - done");

              Console.WriteLine("Main proccess - done");

              Console.ReadKey();
          }

          private static async Task func1()
          {
              Console.WriteLine("Func1 proccess - start");
              await Task.Run(() => Thread.Sleep(1000));
              Console.WriteLine("Func1 proccess - end");
          }

          private static async Task func2()
          {
              Console.WriteLine("Func2 proccess - start");
              await Task.Run(() => Thread.Sleep(3000));
              Console.WriteLine("Func2 proccess - end");
          }

          private static async Task func3()
          {
              Console.WriteLine("Func3 proccess - start");
              await Task.Run(() => Thread.Sleep(5000));
              Console.WriteLine("Func3 proccess - end");
          }

          輸出結(jié)果:

          Async proccess - start
          Async proccess - enter Func1
          Func1 proccess - start
          Async proccess - out Func1
          Async proccess - enter Func2
          Func2 proccess - start
          Async proccess - out Func2
          Async proccess - enter Func3
          Func3 proccess - start
          Async proccess - out Func3
          Async proccess - done
          Main proccess - done
          Func1 proccess - end
          Func2 proccess - end
          Func3 proccess - end

          結(jié)果中,在長時間運(yùn)行Thread.Sleep的時候,跳出去往下執(zhí)行了,是異步。

          ?

          又有問題來了:不是說異步調(diào)用要用await嗎?

          我們把await加到調(diào)用方法的前邊,試一下:

          static async Task Main(string[] args)
          {
              Console.WriteLine("Async proccess - start");

              Console.WriteLine("Async proccess - enter Func1");
              await func1();
              Console.WriteLine("Async proccess - out Func1");

              Console.WriteLine("Async proccess - enter Func2");
              await func2();
              Console.WriteLine("Async proccess - out Func2");

              Console.WriteLine("Async proccess - enter Func3");
              await func3();
              Console.WriteLine("Async proccess - out Func3");

              Console.WriteLine("Async proccess - done");

              Console.WriteLine("Main proccess - done");

              Console.ReadKey();
          }

          跑一下結(jié)果:

          Async proccess - start
          Async proccess - enter Func1
          Func1 proccess - start
          Func1 proccess - end
          Async proccess - out Func1
          Async proccess - enter Func2
          Func2 proccess - start
          Func2 proccess - end
          Async proccess - out Func2
          Async proccess - enter Func3
          Func3 proccess - start
          Func3 proccess - end
          Async proccess - out Func3
          Async proccess - done
          Main proccess - done

          嗯?怎么又像是同步了?

          ?

          對,這是第二個容易錯誤的理解:await是什么意思?

          四、await是什么意思

          提到await,就得先說說Wait。

          字面意思,Wait就是等待。

          前邊說了,異步有一個核心,是Task。而Task有一個方法,就是Wait,寫法是Task.Wait()。所以,很多人把這個Waitawait混為一談,這是錯的

          這個問題來自于Task。C#里,Task不是專為異步準(zhǔn)備的,它表達(dá)的是一個線程,是工作在線程池里的一個線程。異步是線程的一種應(yīng)用,多線程也是線程的一種應(yīng)用。Wait,以及StatusIsCanceled、IsCompleted、IsFaulted等等,是給多線程準(zhǔn)備的方法,跟異步?jīng)]有半毛錢關(guān)系。當(dāng)然你非要在異步中使用多線程的Wait或其它,從代碼編譯層面不會出錯,但程序會。

          尤其,Task.Wait()是一個同步方法,用于多線程中阻塞等待。

          在那個「同步方法中調(diào)用異步方法」的文章中,用Task.Wait()來實(shí)現(xiàn)同步方法中調(diào)用異步方法,這個用法本身就是錯誤的。異步不是多線程,而且在多線程中,多個Task.Wait()使用也會死鎖,也有解決和避免死鎖的一整套方式。

          再說一遍:Task.Wait()是一個同步方法,用于多線程中阻塞等待,不是實(shí)現(xiàn)同步方法中調(diào)用異步方法的實(shí)現(xiàn)方式。

          ?

          說回await。字面意思,也好像是等待。是真的嗎?

          并不是,await不完全是等待的意思。

          在異步中,await表達(dá)的意思是:當(dāng)前線程/方法中,await引導(dǎo)的方法出結(jié)果前,跳出當(dāng)前線程/方法,從調(diào)用當(dāng)前線程/方法的位置,去執(zhí)行其它可能執(zhí)行的線程/方法,并在引導(dǎo)的方法出結(jié)果后,把運(yùn)行點(diǎn)拉回到當(dāng)前位置繼續(xù)執(zhí)行;直到遇到下一個await,或線程/方法完成返回,跳回去剛才外部最后執(zhí)行的位置繼續(xù)執(zhí)行。

          有點(diǎn)繞,還是看代碼:

            static async Task Main(string[] args)
            
          {
          1     Console.WriteLine("Async proccess - start");

          2     Console.WriteLine("Async proccess - enter Func1");
          3     func1();
          4     Console.WriteLine("Async proccess - out Func1");

          5     Console.WriteLine("Async proccess - done");

          6         Thread.Sleep(2000);

          7     Console.WriteLine("Main proccess - done");

          8    Console.ReadKey();
            }

            private static async Task func1()
            
          {
          9     Console.WriteLine("Func1 proccess - start");
          10    await Task.Run(() => Thread.Sleep(1000));
          11    Console.WriteLine("Func1 proccess - end");
            }

          這個代碼,執(zhí)行時是這樣的:順序執(zhí)行1、2、3,進(jìn)到func1,執(zhí)行9、10,到10時,有await,所以跳出,執(zhí)行4、5、6。而6是一個長時等待,在等待的過程中,func1的10運(yùn)行完成,運(yùn)行點(diǎn)跳回10,執(zhí)行11并結(jié)束方法,再回到6等待,結(jié)束等待后繼續(xù)執(zhí)行7、8結(jié)束。

          我們看一下結(jié)果:

          Async proccess - start
          Async proccess - enter Func1
          Func1 proccess - start
          Async proccess - out Func1
          Async proccess - done
          Func1 proccess - end
          Main proccess - done

          映證了這樣的次序。

          ?

          在這個例子中,await在控制異步的執(zhí)行次序。那為什么要用等待這么個詞呢?是因?yàn)?code style="font-size: inherit;line-height: inherit;word-wrap: break-word;padding: 2px 4px;border-top-left-radius: 4px;border-top-right-radius: 4px;border-bottom-right-radius: 4px;border-bottom-left-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background-color: rgb(248, 248, 248);">await確實(shí)有等待結(jié)果的含義。

          這是await的第二層意思。

          五、await的第二層意思:等待拿到結(jié)果

          await確實(shí)有等待的含義。等什么?等異步的運(yùn)行結(jié)果。

          看代碼:

          static async Task Main(string[] args)
          {
              Console.WriteLine("Async proccess - start");

              Console.WriteLine("Async proccess - enter Func1");
              Task<int> f = func1();
              Console.WriteLine("Async proccess - out Func1");

              Console.WriteLine("Async proccess - done");

              int result = await f;

              Console.WriteLine("Main proccess - done");

              Console.ReadKey();
          }

          private static async Task<int> func1()
          {
              Console.WriteLine("Func1 proccess - start");
              await Task.Run(() => Thread.Sleep(1000));
              Console.WriteLine("Func1 proccess - end");

              return 5;
          }

          比較一下這段代碼和上一節(jié)的代碼,很容易搞清楚執(zhí)行過程。

          這個代碼,完成了這樣一個需求:我們需要使用func1方法的返回值。我們可以提前去執(zhí)行這個方法,而不急于拿到方法的返回值,直到我們需要使用時,再用await去獲取到這個返回值去使用。

          ?

          這才是異步對于我們真正的用處。對于一些耗時的IO或類似的操作,我們可以提前調(diào)用,讓程序可以利用執(zhí)行過程中的空閑時間來完成這個操作。等到我們需要這個操作的結(jié)果用于后續(xù)的執(zhí)行時,我們await這個結(jié)果。這時候,如果await的方法已經(jīng)執(zhí)行完成,那我們可以馬上得到結(jié)果;如果沒有完成,則程序?qū)⒗^續(xù)執(zhí)行這個方法直到得到結(jié)果。

          六、同步方法中調(diào)用異步

          正確的方法只有一個:

          func1().GetAwaiter().GetResult();

          這其實(shí)就是await的一個變形。?

          (全文完)

          喜歡就來個三連,讓更多人因你而受益

          往期推薦

          ASP.NET Core 學(xué)習(xí)手冊
          臥槽,超實(shí)用的Visual Studio插件

          2020年7月編程語言排行榜,C#變化不大!


          回復(fù) 【關(guān)閉】學(xué)關(guān)
          回復(fù) 【實(shí)戰(zhàn)】獲取20套實(shí)戰(zhàn)源碼
          回復(fù) 【福利】獲取最新微信支付有獎勵
          回復(fù) 【被刪】學(xué)
          回復(fù) 【訪客】學(xué)
          回復(fù) 【卡通】學(xué)制作微信卡通頭像
          回復(fù) 【python】學(xué)微獲取全套0基礎(chǔ)Python知識手冊
          回復(fù) 【2019】獲取2019 .NET 開發(fā)者峰會資料PPT
          瀏覽 31
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品男女考逼视频 | 四虎影库亚洲无码 | 日逼国产 | 成人网站在线观看视频 | 黄色无遮挡 |