首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >保持CurrentCulture处于异步/等待状态

保持CurrentCulture处于异步/等待状态
EN

Stack Overflow用户
提问于 2015-06-05 09:11:53
回答 3查看 10.3K关注 0票数 30

我有以下伪码

代码语言:javascript
复制
string GetData()
{
  var steps = new List<Task<string>>
  {
    DoSomeStep(),
    DoSomeStep2()
  };

  await Task.WhenAll(steps);

  return SomeResourceManagerProxy.RetrieveValuesForLocalizedStrings( steps.Select(s => s.Result) );

}

此方法是从WebService调用的,在这里,我根据用户的浏览器设置设置Thread.CurrentUICulture

等待之后,CurrentUICulture就丢失了(我运行在不同的线程上)。

我已经用以下方法解决了这个问题:

代码语言:javascript
复制
    public class MyAwaiter<T> : INotifyCompletion
    {
        private TaskAwaiter<T> waiter;
        private CultureInfo culture;

        public MyAwaiter(TaskAwaiter<T> waiter)
        {
            this.waiter = waiter;
        }

        public PreserveCultureAwaiter<T> GetAwaiter() { return this; }

        public bool IsCompleted { get { return waiter.IsCompleted; } }

        public void OnCompleted(Action continuation)
        {
            culture = Thread.CurrentThread.CurrentUICulture;
            waiter.OnCompleted(continuation);
        }

        public T GetResult()
        {
            Thread.CurrentThread.CurrentUICulture = culture;
            return waiter.GetResult();
        }
    }

    public static MyAwaiter<T> KeepCulture<T>(this Task<T> task)
    {
        return new MyAwaiter<T>(task.GetAwaiter());
    }

...

    await Task.WhenAll(steps).KeepCulture();

这有一个缺点--需要记住在等待的每个任务上调用KeepCulture()。(我也有一些扩展方法来保持UI文化的任务)。

有没有更简单的方法来保存UI文化?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-06-05 10:34:22

文化不会在.NET框架中流动,这是一个非常臭名昭著的问题。在Windows上很难解决,区域性是线程的非托管财产,所以CLR不能确保总是正确设置。这使得修改主线程上的CurrentCulture成为一个很大的错误。你得到的bug很难诊断。就像您在一个线程上创建的SortedList一样,它突然不再在另一个线程上排序。糟透了。

微软在.NET 4.5中做了一些事情,他们添加了CultureInfo.DefaultThreadCurrentCulture属性。还有DefaultThreadCurrentUICulture。这仍然不能保证它将被正确设置,您调用的非托管代码可以更改它,而CLR不能对它做任何事情。换句话说,一个bug将很难诊断。但至少你知道什么时候可能会变。

更新:在.NET 4.6中彻底解决了这个问题,区域性现在从一个线程流到另一个线程,CultureInfo.DefaultThreadCurrentCulture攻击不再是必要的,也不再有用。在用于CultureInfo.CurrentCulture的MSDN文章中有文档。现在编写的细节似乎并不完全正确,在我测试它时,它总是源源不断地流着,DefaultThreadCurrentCulture似乎不再起任何作用了。

票数 43
EN

Stack Overflow用户

发布于 2015-06-05 11:28:05

到目前为止,我已经创建了自己的SynchronizationContext,我已经用ASP.NET和控制台应用程序进行了测试,在这两种应用程序中,它都保持了我想要的文化:

代码语言:javascript
复制
/// <summary>
/// Class that captures current thread's culture, and is able to reapply it to different one
/// </summary>
internal sealed class ThreadCultureHolder
{
    private readonly CultureInfo threadCulture;
    private readonly CultureInfo threadUiCulture;

    /// <summary>
    /// Captures culture from currently running thread
    /// </summary>
    public ThreadCultureHolder()
    {
        threadCulture = Thread.CurrentThread.CurrentCulture;
        threadUiCulture = Thread.CurrentThread.CurrentUICulture;
    }

    /// <summary>
    /// Applies stored thread culture to current thread
    /// </summary>
    public void ApplyCulture()
    {
        Thread.CurrentThread.CurrentCulture = threadCulture;
        Thread.CurrentThread.CurrentUICulture = threadUiCulture;
    }

    public override string ToString()
    {
        return string.Format("{0}, UI: {1}", threadCulture.Name, threadUiCulture.Name);
    }
}

/// <summary>
/// SynchronizationContext that passes around current thread's culture
/// </summary>
internal class CultureAwareSynchronizationContext : SynchronizationContext
{
    private readonly ThreadCultureHolder cultureHolder;
    private readonly SynchronizationContext synchronizationImplementation;

    /// <summary>
    /// Creates default SynchronizationContext, using current(previous) SynchronizationContext 
    /// and captures culture information from currently running thread
    /// </summary>
    public CultureAwareSynchronizationContext()
        : this(Current)
    {}

    /// <summary>
    /// Uses passed SynchronizationContext (or null, in that case creates new empty SynchronizationContext) 
    /// and captures culture information from currently running thread
    /// </summary>
    /// <param name="previous"></param>
    public CultureAwareSynchronizationContext(SynchronizationContext previous)
        : this(new ThreadCultureHolder(), previous)
    {
    }

    internal CultureAwareSynchronizationContext(ThreadCultureHolder currentCultureHolder, SynchronizationContext currentSynchronizationContext)
    {
        cultureHolder = currentCultureHolder;
        synchronizationImplementation = currentSynchronizationContext ?? new SynchronizationContext();
    }

    public override void Send(SendOrPostCallback d, object state)
    {
        cultureHolder.ApplyCulture();
        synchronizationImplementation.Send(d, state);
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        synchronizationImplementation.Post(passedState =>
        {
            SetSynchronizationContext(this);
            cultureHolder.ApplyCulture();
            d.Invoke(s);
        }, state);
    }

    public override SynchronizationContext CreateCopy()
    {
        return new CultureAwareSynchronizationContext(cultureHolder, synchronizationImplementation.CreateCopy());
    }

    public override string ToString()
    {
        return string.Format("CultureAwareSynchronizationContext: {0}", cultureHolder);
    }
}

用法:

代码语言:javascript
复制
/// code that detects Browser's culture 
void Detection()
{
        Thread.CurrentThread.CurrentUICulture = new CultureInfo("cs");
        SynchronizationContext.SetSynchronizationContext(new CultureAwareSynchronizationContext());
}

此解决方案可能存在汉斯·帕桑特提到的问题

票数 4
EN

Stack Overflow用户

发布于 2021-01-15 14:06:55

很好的解释是在官方医生中。

对于.Net 4.6+,您需要在调用线程中设置在await之前的区域性。因此,区域性将从当前线程传递到所有下一个async/await (作为后续线程读取)。

代码语言:javascript
复制
public async Task SomeFunction(){
  Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(locale);
  Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture;
  await OtherFunction();
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30662668

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档