根据this question async/await
中的答案,调用应保留CurrentCulture。就我而言,当我调用自己的异步方法时,将保留CurrentCulture。但是,如果我调用某种WCF服务方法,则不会保留CurrentCulture,它会被更改为看起来像服务器默认线程文化的东西。
我已经了解了调用托管线程的内容。它发生在每个代码行都在一个托管线程上执行(ManagedThreadId保持不变)。在调用我自己的异步方法后,CultureInfo保持不变。但是,当我调用WCF的方法时,ManagedTrheadId保持不变,但CurrentCulture已更改。
简化代码如下所示::
private async Task Test()
{
// Befoe this code Thread.CurrentThread.CurrentCulture is "ru-RU", i.e. the default server's culture
// Here we change current thread's culture to some desired culture, e.g. hu-HU
Thread.CurrentThread.CurrentCulture = new CultureInfo("hu-HU");
var intres = await GetIntAsync();
// after with await Thread.CurrentThread.CurrentCulture is still "hu-HU"
var wcfres = await wcfClient.GetResultAsync();
// And here Thread.CurrentThread.CurrentCulture is "ru-RU", i.e. the default server's culture
}
private async Task<int> GetIntAsync()
{
return await Task.FromResult(1);
}
wcfClient
是自动生成的WCF客户端的实例(System.ServiceModel.ClientBase
的继承者)
这一切都发生在ASP.NET MVC Web API(自托管)中,我使用Accept-Language标头将CurrentCulture设置为稍后访问它并使用它来返回本地化资源。我可以在没有CurrentCulture的情况下继续前进,只是将CultureInfo传递给每个方法,但我不喜欢这种方法。
为什么CurrentThread的CurrentCulture在调用WCF服务后被更改,并在调用我自己的异步方法后保持不变?可以“修复”吗?
答案 0 :(得分:2)
将文化重置为await
之前的文化通常是同步上下文的工作。但是因为(据我所知),你没有任何同步上下文,await
不会以任何方式修改线程文化。
这意味着如果你在await
之后回到同一个帖子,你就会看到你设定的文化。但是如果你在另一个线程上恢复,你将看到默认文化(除非其他人为该线程修改了它)。
如果没有同步上下文,您何时会在await
之后进入同一个线程?如果异步操作同步完成(例如GetIntAsync()
),则可以保证在同一个线程上,因为在这种情况下,该方法仅在await
之后同步。如果幸运的话,你也可以在同一个帖子上继续,但你不能依赖它。这可能是不确定的,因此您的代码有时似乎有效,有时则不然。
如果您希望使用await
流动文化并且没有为您执行此操作的同步上下文,您应该怎么做?基本上,您有两种选择:
WithCulture()
)。这意味着您需要将此添加到您需要流动文化的所有await
,这可能很麻烦。await
传输文化。这意味着您只能为每个操作设置一次上下文,它将正常工作(类似于ASP.NET同步上下文所做的)。从长远来看,这可能是更好的解决方案。答案 1 :(得分:1)
我没有明确回答为什么会出现所描述的行为,特别是考虑到整个调用链保持在同一个线程(ManagedThreadId
保持不变)的说法。此外,我的假设是文化不会随着AspNetSynchronizationContext
下的执行上下文而流动是错误的,它实际上是流动的,实际上是。我从@ StephenCleary关于尝试await Task.Delay
的评论中得到了一点,并通过以下一些小研究证实了这一点:
// GET api/values/5
public async Task<string> Get(int id)
{
// my default culture is en-US
Log("Get, enter");
Thread.CurrentThread.CurrentCulture = new CultureInfo("hu-HU");
Log("Get, before Task.Delay");
await Task.Delay(200);
Thread.Sleep(200);
Log("Get, before Task.Run");
await Task.Run(() => Thread.Sleep(100));
Thread.Sleep(200);
Log("Get, before Task.Yield");
await Task.Yield();
Log("Get, before exit");
return "value";
}
static void Log(string message)
{
var ctx = SynchronizationContext.Current;
Debug.Print("{0}; thread: {1}, context: {2}, culture {3}",
message,
Thread.CurrentThread.ManagedThreadId,
ctx != null ? ctx.GetType().Name : String.Empty,
Thread.CurrentThread.CurrentCulture.Name);
}
<强>输出:强>
Get, enter; thread: 12, context: AspNetSynchronizationContext, culture en-US Get, before Task.Delay; thread: 12, context: AspNetSynchronizationContext, culture hu-HU Get, before Task.Run; thread: 11, context: AspNetSynchronizationContext, culture hu-HU Get, before Task.Yield; thread: 10, context: AspNetSynchronizationContext, culture hu-HU Get, before exit; thread: 11, context: AspNetSynchronizationContext, culture hu-HU
因此,我只能想象wcfClient.GetResultAsync()
内的某些内容实际上会改变当前线程的文化。解决方法可能是使用Stephen Toub's CultureAwaiter
之类的客户等待。然而,这种症状令人担忧。也许您应该搜索生成的WCF客户端代理代码以查找“文化”并检查其中发生了什么。尝试单步执行,找出Thread.CurrentThread.CurrentCulture
重置的位置。