亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

為什么這些 C# 異步方法不執行過去的 Task.Delay()?

為什么這些 C# 異步方法不執行過去的 Task.Delay()?

C#
白衣染霜花 2022-12-31 13:28:50
我試圖理解 C# async/await 并觀察到這種令人困惑的行為,其中異步方法不執行過去Task.Delay的調用??紤]以下 -class Program{    static void Main(string[] args)    {        Program p = new Program();        p.MakeBreakfast();    }    public async Task MakeBreakfast()    {        await BoilWater();        StartToaster();        PutTeainWater();        PutBreadinToaster();        SpreadButter();    }    public async Task BoilWater() { Console.WriteLine("BoilWater start"); await Task.Delay(30); Console.WriteLine("BoilWater end"); }    public async Task StartToaster() { Console.WriteLine("StartToaster start"); await Task.Delay(1); Console.WriteLine("StartToaster end"); }    public async Task PutBreadinToaster() { Console.WriteLine("PutBreadinToaster start"); await Task.Delay(2000); Console.WriteLine("PutBreadinToaster end"); }    public async Task PutTeainWater() { Console.WriteLine("PutTeainWater start"); await Task.Delay(30); Console.WriteLine("PutTeainWater end"); }    public async Task SpreadButter() { Console.WriteLine("SpreadButter start"); await Task.Delay(10); Console.WriteLine("SpreadButter end"); }}它的輸出將是 -Boilwater StartPress any key to continue...“Boilwater end”語句和所有其他方法調用發生了什么?如果我只將 async/await 放在 BoilWater 方法中,我會得到相同的輸出。如果我從所有方法調用中刪除 await -    public async Task MakeBreakfast()    {         BoilWater();         StartToaster();         PutTeainWater();         PutBreadinToaster();         SpreadButter();    }Now, the output is - BoilWater startStartToaster startPutTeainWater startPutBreadinToaster startSpreadButter startPress any key to continue . . .現在,“結束”語句發生了什么?在這些示例中,async await 發生了什么?
查看完整描述

3 回答

?
慕容708150

TA貢獻1831條經驗 獲得超4個贊

您的程序從調用開始,Main完成后退出。


因為Main只是創建了一個實例,Program然后調用MakeBreakfast(),它Task會在遇到第一個時立即返回到 main await。因此Main幾乎立即存在。


讓我們稍微更改一下代碼,看看是否是這種情況:


static void Main(string[] args)

{

    Program p = new Program();

    p.MakeBreakfast();

    Console.WriteLine("Done!");

    Console.ReadLine();

}


public async Task MakeBreakfast()

{

    Console.WriteLine("Starting MakeBreakfast");

    Thread.Sleep(1000);

    Console.WriteLine("Calling await BoilWater()");

    await BoilWater();

    Console.WriteLine("Done await BoilWater()");

    StartToaster();

    PutTeainWater();

    PutBreadinToaster();

    SpreadButter();

}

現在,如果我讓它運行完成,我會看到這個輸出:


Starting MakeBreakfast

Calling await BoilWater()

BoilWater start

Done!

BoilWater end

Done await BoilWater()

StartToaster start

PutTeainWater start

StartToaster end

PutBreadinToaster start

SpreadButter start

SpreadButter end

PutTeainWater end

PutBreadinToaster end

代碼確實點擊了await然后返回到Main。


為了使代碼正確完成,我們需要await一切。你有兩種方法可以做到這一點:


(1)


static async Task Main(string[] args)

{

    Program p = new Program();

    await p.MakeBreakfast();

    Console.WriteLine("Done!");

    Console.ReadLine();

}


public async Task MakeBreakfast()

{

    await BoilWater();

    await StartToaster();

    await PutTeainWater();

    await PutBreadinToaster();

    await SpreadButter();

}

現在當它運行時你會得到這個輸出:


BoilWater start

BoilWater end

StartToaster start

StartToaster end

PutTeainWater start

PutTeainWater end

PutBreadinToaster start

PutBreadinToaster end

SpreadButter start

SpreadButter end

Done!

(2)


static async Task Main(string[] args)

{

    Program p = new Program();

    await p.MakeBreakfast();

    Console.WriteLine("Done!");

    Console.ReadLine();

}


public async Task MakeBreakfast()

{

    var tasks = new[]

    {

        BoilWater(),

        StartToaster(),

        PutTeainWater(),

        PutBreadinToaster(),

        SpreadButter(),

    };

    await Task.WhenAll(tasks);

}

現在這個版本同時開始所有的早餐任務,但在返回之前等待它們全部完成。


你得到這個輸出:


BoilWater start

StartToaster start

PutTeainWater start

PutBreadinToaster start

SpreadButter start

StartToaster end

SpreadButter end

BoilWater end

PutTeainWater end

PutBreadinToaster end

Done!

一種更符合邏輯的代碼執行方式——先燒水,再泡茶;然后啟動烤面包機,煮吐司,攤開吐司——可能是這樣的:


public async Task MakeBreakfast()

{

    async Task MakeTea()

    {

        await BoilWater();

        await PutTeainWater();      

    }


    async Task MakeToast()

    {

        await StartToaster();

        await PutBreadinToaster();

        await SpreadButter();           

    }       

    await Task.WhenAll(MakeTea(), MakeToast());

}

這給出了:


BoilWater start

StartToaster start

StartToaster end

PutBreadinToaster start

BoilWater end

PutTeainWater start

PutTeainWater end

PutBreadinToaster end

SpreadButter start

SpreadButter end

Done!


查看完整回答
反對 回復 2022-12-31
?
躍然一笑

TA貢獻1826條經驗 獲得超6個贊

異步方法的一般工作流程是同步運行 await 之前的代碼(即按原樣運行),然后返回一個任務對象,其中包含要等待的任務,而 await 之后的所有內容都作為該任務的延續任務完成時執行。

現在,如果只BoilWater等待啟動消息同步執行,所有其他調用將作為延續。由于MakeBreakfast沒有等待,程序將在BoilWater完成/等待它們的毫秒之前執行,因此不會執行延續(即其他任務)。

如果BoilWater沒有等待,則其他MakeBreakfast任務不會作為BoilWater任務的延續。這意味著BoilWater再次運行直到 Task.Delay 并將其作為任務返回。但是,由于沒有等待此任務,因此下一個任務MakeBreakfast將以相同的方式啟動。所以本質上,所有MakeBreakfast任務都是按順序啟動的,并且MakeBreakfast只能在SpreadWater啟動時返回并返回它的任務。同樣,任務仍在后臺運行,等待它們的毫秒數,但程序在此時間范圍之前退出,因此關閉消息延續沒有機會運行。


查看完整回答
反對 回復 2022-12-31
?
青春有我

TA貢獻1784條經驗 獲得超8個贊

“Boilwater end”語句和所有其他方法調用發生了什么?如果我只將 async/await 放在 BoilWater 方法中,我會得到相同的輸出。

正如 zerkms 所說,您的程序在任務完成之前退出。

如果我從所有方法調用中刪除 await -

我相信如果await沒有在異步任務中的任何地方調用,那么它只是同步處理它;這可以解釋為什么輸出顯示所有“開始”消息。

至于“結束”語句發生了什么,我相信如果你不等待Task.Delay,那么它實際上不會等待(延遲)并且你的代碼將繼續。實際上我剛剛發現這個可以更好地解釋它。


查看完整回答
反對 回復 2022-12-31
  • 3 回答
  • 0 關注
  • 241 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號