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

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

無法繼續使用捕獲的 GUI 上下文,但為什么會出現死鎖?

無法繼續使用捕獲的 GUI 上下文,但為什么會出現死鎖?

C#
幕布斯6054654 2022-12-24 09:44:54
我想知道為什么在以下情況下沒有繼續使用捕獲的 GUI 上下文時會出現死鎖。public Form1(){    InitializeComponent();    CheckForIllegalCrossThreadCalls = true;}async Task DelayAsync(){    // GUI context is captured here (right before the following await)    await Task.Delay(3000);//.ConfigureAwait(false);    // As no  code follows the preceding await, there is no continuation that uses the captured GUI context. }private async void Button1_Click(object sender, EventArgs e){    Task t = DelayAsync();    t.Wait();}編輯:我知道僵局可以通過任何一種方式解決使用await Task.Delay(3000).ConfigureAwait(false);或替換t.Wait();為await t;.但這不是問題。問題是為什么沒有繼續使用捕獲的 GUI 上下文時會出現死鎖?在我的心智模型中,如果有繼續,那么它將使用捕獲的 GUI 上下文,因此會導致死鎖。
查看完整描述

1 回答

?
哆啦的時光機

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

async與服務員一起工作,而不是與任務一起工作。因此,在方法末尾需要一些額外的邏輯來將等待者的狀態轉換為任務。


您認為沒有延續的假設是錯誤的。如果您剛剛返回任務,那將是正確的:


Task DelayAsync()

{

    return Task.Delay(3000);

}

但是,當您將方法標記為 時,事情會變得更加復雜async。方法的一個重要屬性async是它處理異常的方式。例如考慮這些方法:


Task NoAsync()

{

    throw new Exception();

}


async Task Async()

{

    throw new Exception();

}

現在如果你調用它們會發生什么?


var task1 = NoAsync(); // Throws an exception

var task2 = Async(); // Returns a faulted task

不同之處在于異步版本將異常包裝在返回的任務中。


它與我們的案例有什么關系?


當您await使用方法時,編譯器實際上會調用GetAwaiter()您正在等待的對象。等待者定義了 3 個成員:


該IsCompleted物業

OnCompleted方法_

GetResult方法_

可以看到,沒有成員直接返回異常。如何知道服務員是否有故障?要知道這一點,您需要調用GetResult將拋出異常的方法。


回到你的例子:


async Task DelayAsync()

{

    await Task.Delay(3000);

}

如果Task.Delay拋出異常,async機器需要將返回任務的狀態設置為故障。要知道是否Task.Delay拋出異常,需要在完成GetResult后調用等待Task.Delay者。因此你有一個延續,雖然在看到代碼時并不明顯。在幕后,異步方法看起來像:


Task DelayAsync()

{

    var tcs = new TaskCompletionSource<object>();


    try

    {

        var awaiter = Task.Delay(3000).GetAwaiter();


        awaiter.OnCompleted(() =>

        {

            // This is the continuation that causes your deadlock

            try

            {

                awaiter.GetResult();

                tcs.SetResult(null);

            }

            catch (Exception ex)

            {

                tcs.SetException(ex);

            }

        });

    }

    catch (Exception ex)

    {

        tcs.SetException(ex);

    }


    return tcs.Task;

}

實際代碼比較復雜,用aAsyncTaskMethodBuilder<T>代替了TaskCompletionSource<T>,但是思路是一樣的。


查看完整回答
反對 回復 2022-12-24
  • 1 回答
  • 0 關注
  • 81 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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