【发布时间】:2021-11-15 14:05:25
【问题描述】:
据我了解,AsyncLocal 提供了跨任务保持其值的能力,因此我希望以下示例代码在对象被序列化时保持 Context.Culture 值,但事实并非如此。
在我看来,鉴于PropertyDisplay.Text 有一个getting,它的执行方式“不同”,因此Context.Culture 的值会丢失。
我的假设正确吗? 如果没有,有没有办法做到这一点?
预期输出
{
"text": "prefix-zh-CHS"
}
实际输出
{
"text": "prefix"
}
示例代码:
private static readonly Dictionary<string, string> LanguageMapping = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "zh", "zh-CHS" }
};
static async Task Main(string[] args)
{
PropertyDisplay propertyDisplay = null;
string json = null;
ExecutionContext.SuppressFlow();
await Task.Run(() =>
{
Context.Culture = GetCultureInfo("zh");
});
await Task.Run(() =>
{
propertyDisplay = new PropertyDisplay()
{
Text = "prefix"
};
});
await Task.Run(() =>
{
json = JsonConvert.SerializeObject(propertyDisplay, Formatting.Indented, new JsonSerializerSettings()
{
});
});
Console.WriteLine(json);
Console.ReadKey();
}
public static CultureInfo GetCultureInfo(string language)
{
CultureInfo retVal;
if (language != null && LanguageMapping.ContainsKey(language))
{
language = LanguageMapping[language];
}
try
{
retVal = new CultureInfo(language);
}
catch
{
retVal = new CultureInfo("en-us");
}
return retVal;
}
public static class Context
{
private static AsyncLocal<CultureInfo> culture = new AsyncLocal<CultureInfo>();
public static CultureInfo Culture
{
get
{
return culture?.Value;
}
set
{
culture.Value = value;
}
}
}
public class PropertyDisplay
{
private string displaySelectionText;
[JsonProperty(PropertyName = "text")]
public string Text
{
get
{
if (string.IsNullOrWhiteSpace(this.displaySelectionText))
{
return null;
}
var localized = GetLocalizedString(this.displaySelectionText);
return localized;
}
set
{
this.displaySelectionText = value;
}
}
public static string GetLocalizedString(string configText)
{
string retVal = configText;
if (Context.Culture != null)
{
retVal = $"{configText}-{Context.Culture}";
}
return retVal;
}
}
更新
看起来如果我打电话
ExecutionContext.SuppressFlow() 然后
它保持该值并按预期工作。
想知道是否有人可以解释它是如何工作的。
【问题讨论】:
-
AsyncLocal.Value实际上存储在ExecutionContext(github.com/dotnet/runtime/blob/…) 中。我不确定Task.Run是否克隆了ExecutionContext? -
您能否与我们分享更新后的代码在哪里使用
SuppressFlow()?请记住,还有一个RestoreFlow方法,它只能从调用SuppressFlow方法的同一线程中调用。 -
但坦率地说,我完全不明白你为什么要使用
AsyncLocal。AsyncLocal意味着(或多或少)每个异步上下文都将看到同一变量的自己的本地版本。但是您正试图将其用作它们之间的共享资源。 -
如果您想将
CultureInfo设置在不同的任务上,而不是您想从中检索它,那么忘记AsyncLocal。将其用作“普通”共享资源。 -
@HoBa 您的真正问题是如何在序列化期间本地化文本属性,而不是如何序列化
AsyncLocal字段。此外,您的PropertyValue没有任何AsyncLocal字段。如果您使用async/await,当前的文化已经从一个任务流向另一个任务。AsyncLocal与您的要求相反。
标签: c# multithreading serialization task