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

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

ExecutionContext 不會從異步方法向上流動調用堆棧

ExecutionContext 不會從異步方法向上流動調用堆棧

C#
慕姐8265434 2022-11-22 16:01:43
考慮以下代碼:private static async Task Main(string[] args){    await SetValueInAsyncMethod();    PrintValue();    await SetValueInNonAsyncMethod();    PrintValue();}private static readonly AsyncLocal<int> asyncLocal = new AsyncLocal<int>();private static void PrintValue([CallerMemberName] string callingMemberName = ""){    Console.WriteLine($"{callingMemberName}: {asyncLocal.Value}");}private static async Task SetValueInAsyncMethod(){    asyncLocal.Value = 1;    PrintValue();    await Task.CompletedTask;}private static Task SetValueInNonAsyncMethod(){    asyncLocal.Value = 2;    PrintValue();    return Task.CompletedTask;}如果您在 .NET 4.7.2 控制臺應用程序中運行此代碼,您將獲得以下輸出:SetValueInAsyncMethod: 1Main: 0SetValueInNonAsyncMethod: 2Main: 2我確實理解輸出的差異源于這樣一個事實,即SetValueInAsyncMethod它不是真正的方法,而是一個執行的狀態機,它在內部AsyncTaskMethodBuilder捕獲并且只是一個常規方法。ExecutionContextSetValueInNonAsyncMethod但即使有了這種理解,我仍然有一些問題:這是錯誤/缺失的功能還是有意的設計決定?在編寫依賴于 的代碼時,我是否需要擔心這種行為AsyncLocal?比如說,我想編寫我的TransactionScope-wannabe,它通過等待點來傳輸一些環境數據。AsyncLocal這里夠了嗎?當歸結為在整個“邏輯代碼流”中保留值時,在 .NET 中是否還有其他替代方法AsyncLocal和CallContext.LogicalGetData/ ?CallContext.LogicalSetData
查看完整描述

2 回答

?
慕尼黑5688855

TA貢獻1848條經驗 獲得超2個贊

這是錯誤/缺失的功能還是有意的設計決定?

這是一個有意的設計決定。具體來說,async狀態機為其邏輯上下文設置“寫時復制”標志。

與此相關的是所有同步方法都屬于它們最近的祖先async方法。

在編寫依賴于 AsyncLocal 的代碼時,我是否需要擔心這種行為?比如說,我想編寫我的 TransactionScope-wannabe,它通過等待點傳輸一些環境數據。這里 AsyncLocal 夠用嗎?

像這樣的大多數系統都AsyncLocal<T>結合了IDisposable清除AsyncLocal<T>值的模式。組合這些模式可確保它適用于同步或異步代碼。AsyncLocal<T>如果消費代碼是一個async方法,它自己會工作得很好;與它一起使用IDisposable確保它可以同時使用async和同步方法。

當涉及到在整個“邏輯代碼流”中保留值時,.NET 中的 AsyncLocal 和 CallContext.LogicalGetData / CallContext.LogicalSetData 是否還有其他替代方案?

不。


查看完整回答
反對 回復 2022-11-22
?
拉丁的傳說

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

這對我來說似乎是一個有意的決定。


正如您已經知道的那樣,它SetValueInAsyncMethod被編譯成一個隱式捕獲當前 ExecutionContext 的狀態機。當您更改AsyncLocal-variable 時,該更改不會“流”回調用函數。相反,SetValueInNonAsyncMethod它不是異步的,因此沒有編譯成狀態機。因此,不會捕獲 ExecutionContext 并且對AsyncLocal-variables 的任何更改對調用者都是可見的。


如果您出于任何原因需要它,您也可以自己捕獲 ExecutionContext:


private static Task SetValueInNonAsyncMethodWithEC()

{

    var ec = ExecutionContext.Capture(); // Capture current context into ec

    ExecutionContext.Run(ec, _ => // Use ec to run the lambda

    {

        asyncLocal.Value = 3;

        PrintValue();

    });

    return Task.CompletedTask;

}

這將輸出值 3,而 Main 將輸出 2。


當然,簡單地轉換SetValueInNonAsyncMethod為異步讓編譯器為您做這件事要容易得多。


關于使用AsyncLocal(或就此而言CallContext.LogicalGetData)的代碼,重要的是要知道更改調用的異步方法(或任何捕獲的 ExecutionContext)中的值不會“回流”。但您當然仍然可以訪問和修改AsyncLocal,只要您不重新分配它即可。


查看完整回答
反對 回復 2022-11-22
  • 2 回答
  • 0 關注
  • 140 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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