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

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

C# 編譯器不正確的異步,等待?

C# 編譯器不正確的異步,等待?

C#
jeck貓 2022-01-09 15:13:43
我不是在問問題,而是在討論 C# 編譯器中的錯誤/錯誤。// do a heavy job it take 2s and return "1"public async Task<string> DoJob() {    var task = new Task<string>(() => {        Thread.Sleep(2000);        return "1";    });    task.Start();    return await task;}private async void button1_Click(object sender, EventArgs e) {    var task= DoJob();    textBox1.Text += await task;    this.Text += "2";}當我點擊 button1 3 次時,我期望:textBox1.Text == "111" this.Text == "222"但結果是:textBox1.Text == "1" this.Text == "222"還有另一個錯誤,在等待 2 秒(在 2 秒之前)時,我通過輸入鍵盤更改了 textBox1.Text,但結果仍然相同“1”,而不是附加到文本末尾(+= 運算符)。根據我的知識 async 和 await 是關鍵字什么都不做,只是幫助編譯器知道將代碼放入塊的位置(糾正我):例子輸入:private async void button1_Click(object sender, EventArgs e) {    var task= DoJob();    textBox1.Text += await task;    this.Text += "2";}輸出:這給出了我期望的結果,但與 C# 編譯器不同。而且這也沒有上面還有一個bug。private void button1_Click(object sender, EventArgs e) {    var task= DoJob();    task.ContinueWith((_task) => {        this.Invoke(new Action(() => {            textBox1.Text += _task.Result;            this.Text += "2";        }));    });}但是 MS C# 編譯器會做這樣的事情:private void button1_Click(object sender, EventArgs e) {    var task = DoJob();    var left = textBox1.Text;    task.ContinueWith((_task) => {                // textBox1.Text += await task;        this.Invoke(new Action(() => {            //            textBox1.Text = left + _task.Result;  //             this.Text += "2";                     // this.Text += "2";        }));    });}您認為這是錯誤還是微軟故意這樣做?
查看完整描述

3 回答

?
阿波羅的戰車

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

考慮以下兩條路徑:


textBox1.Text += await task;

對比


string newText = await task;

textBox1.Text += newText;

在第一個示例中,textBox1.Text 的當前值首先被所有三次單擊立即讀取,這可能只是原始的空文本框值。然后,當每個完成時,它會寫入原始值(就像讀取時一樣,提醒仍然是空字符串)加上您要連接的“1”。所以如果你快速點擊按鈕,三個線程都會寫入“”+“1”的值


在第二個示例中,您等待獲取要連接的結果文本,然后(在 UI 線程上)執行 += 操作,該操作將不間斷地讀取、連接和寫入。


如果您在 += 中等待,則在開始等待之前讀取原始值,并將結果連接應用于保存在等待上下文中的值。


現在這有意義嗎?


查看完整回答
反對 回復 2022-01-09
?
慕仙森

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

某些語言(例如 C++)允許編譯器選擇在給定語句中計算的順序表達式。但是 C# 消除了這種歧義:評估總是從左到右。

textBox1.Text += await task;

在 C# 中,沒有特殊的 += 運算符,它只是擴展為:

textBox1.Text = textBox1.Text + await task;

現在,如果我們從左到右評估,它應該評估的第一件事是 textBox1.set_Text 用于賦值。如果您要在右側執行一些會更改 textBox1 的值的操作,您最終仍會分配給原始 textBox1,而不是新值。

接下來要評估的是 textBox1.get_Text。這應該返回您的“”值。

然后它評估“等待任務”。此時,它陷入等待,無法繼續評估語句,因此它將當前堆棧幀的重要內容轉儲到堆對象中以備后用。當它回來繼續評估時,我們現在評估了以下信息:

  1. 我們要寫入的對象的地址。

  2. 字符串值“”。

  3. 字符串值“1”。

剩下要做的就是將捕獲的“”添加到等待的“1”,并將其分配給捕獲的 textBox1。

如果您快速連續多次執行此操作,則所有 3 個輸入每次都將相同。換句話說,它的行為與標準所描述的完全一致。人們給出的建議將其放在上一行,如下所示:

var temp = await task;
textBox1.Text += temp;

這只是將等待移到 textBox1.Text 之前。您實際上可以進行重新排序內聯并給出預期的結果,只需顛倒語句中表達式的順序即可:

textBox1.Text = string.Concat(
    new[] { await task, textBox1.Text }.Reverse());


查看完整回答
反對 回復 2022-01-09
?
呼喚遠方

TA貢獻1856條經驗 獲得超11個贊

這里沒有編譯器錯誤。但是,這條線是你的問題

textBox1.Text += await task;

如果您更改以下內容,您會發現一致的結果。

var result = await task;
textBox1.Text += result;

一個簡單的類比是您向某人詢問結果(例如單詞或數字),當您完成自己的工作后,您希望將結果添加到他們的結果中。當您非??斓囟啻螆绦写瞬僮鲿r,就會出現問題。你得到了他們原來的價值 3 次(你說,給我你所擁有的,給我你所擁有的,給我你所擁有的),你要去做你的 3 個獨立的工作,當你回來時你只需添加它到原始值(未修改的值),這似乎有效地覆蓋了該值并且不起作用。


查看完整回答
反對 回復 2022-01-09
  • 3 回答
  • 0 關注
  • 163 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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