在 .Net 中:将 CurrentCulture 保持在新线程上的最佳方式?

Posted

技术标签:

【中文标题】在 .Net 中:将 CurrentCulture 保持在新线程上的最佳方式?【英文标题】:In .Net: best way for keeping CurrentCulture on new Thread? 【发布时间】:2011-02-28 16:00:02 【问题描述】:

在 .Net 4.0 WPF 项目中,我们需要在每个线程上保持与主线程相同的 CurrentCulture。

鉴于,我们可以使用如下代码初始化一个新线程的文化:

    将信息保存在变量(上下文)中

    context.CurrentCulture = Thread.CurrentThread.CurrentCulture;
    context.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
    

    在新线程上,从保存的上下文中初始化

    Thread.CurrentThread.CurrentCulture = context.CurrentCulture;
    Thread.CurrentThread.CurrentUICulture = context.CurrentUICulture;
    

但在这个 TPL、异步编程和 lambda 委托的时代,感觉不对。

是的,我们实际上可以在应用程序运行时改变文化,但那是另一回事了。

您知道我们应该初始化以跟踪的任何设置、属性或配置吗?

【问题讨论】:

【参考方案1】:

没有好办法,不惜一切代价避免这种情况。根本问题是文化不是 Thread.ExecutionContext 的一部分,它不会从一个线程流向另一个线程。这是一个无法解决的问题,文化是原生 Windows 线程的属性。它将始终初始化为在控制面板的区域和语言小程序中选择的系统文化。

对文化进行临时的线程本地更改很好,尝试将“流程”切换到另一种文化是您几个月来寻找的错误的来源。字符串排序顺序是最糟糕的问题来源。


编辑:此问题已在 .NET 4.5 中通过 CultureInfo.DefaultThreadCurrentCulture 和 DefaultThreadCurrentUICulture 属性得到修复。


编辑:在 .NET 4.6 中获得了真正的解决方案,文化现在从一个线程流向另一个线程。有关详细信息,请查看 CultureInfo.CurrentCulture 的 MSDN 文章。请注意,描述与行为不完全匹配,需要进行测试。

【讨论】:

并在 .NET 4.6 中进一步修复,成为 ExecutionContext 的一部分。【参考方案2】:

我不明白为什么帕桑特先生对此提出警告,同时又说对文化进行“临时”“线程本地”更改是可以的。没有线程文化是真正的线程本地 - 正如我们所见,任何可以通过公共属性引用线程的东西都可以使用它。而如果短时间换就OK了,那为什么换长一点就不行了呢?你在哪里越界,为什么?

我也不真正理解 OP 的感觉,即“没有必要”编写代码来复制他想要复制的东西。您可能希望将此代码放在可以重用它的地方,但除此之外,我真的看不出代码有问题。在我的书中,它比我见过的任何 lambda 表达式都更加直接和美妙,而且它可以很好地完成这项工作。至少,为了花哨而编写花哨的代码不是我的风格。

你可以这样做:

// Program.cs
static CultureInfo culture, uiCulture;

[STAThread]
static public void Main()

   var t = Thread.CurrentThread;
   culture = t.CurrentCulture;
   uiCulture = t.CurrentUICulture;


static public Thread CreateThread() 

    return new Thread()  CurrentCulture = culture, CurrentUICulture = uiCulture ; 

【讨论】:

而在您的控制之外创建的线程,例如线程池线程,CurrentCulture 变得更加丑陋。 @CodeInChaos 在ThreadPool.QueueUserWorkItem(...) 的委托中,您可以使用静态Thread.CurrentThread.CurrentCulture = myCulture 然后。您还可以将所有内容封装在辅助方法中。【参考方案3】:

顺便说一句,我突然意识到,虽然默认文化来自 Windows 的“区域设置”,但它可能会在 .NET 配置中被覆盖。

如果这是一个 ASP.NET 应用程序,您将使用 web.config 中的 globalization 元素。我可能会从这里开始学习 WPF 中的本地化:

http://msdn.microsoft.com/en-us/library/ms788718.aspx#workflow_to_localize

(是我,还是微软使用全球化和本地化这两个词,相当混乱,可以互换?)

【讨论】:

其实你最后一个问题的答案是否定的,全球化就是为后续本地化做准备的过程。 IE。应用程序可能是完全全球化的,并且除了默认的本地化之外没有可用的本地化。【参考方案4】:

当我使用 TPL 创建任务时,我总是传递当前 UI 线程的文化。请参阅下面的示例代码。

private void WorkProcessingAsync(IWorkItem workItem)
        
            IsBusy = true;
            /* =============================
            *  Create a TPL Task and pass the current UiCulture in an state Object to resolve the correct .resx file for translation / globalisation / Multilanguate features in Background Thread
            * ==============================*/
            Task<IWorkItem> task = Task.Factory.StartNew((stateObj) =>
            
                // here we are already in the task background thread
                // save cast the given stateObj
                var tuple = stateObj as Tuple<IWorkItem, CultureInfo>;

                Debug.Assert(tuple != null, "tuple != null");

                Thread.CurrentThread.CurrentUICulture = tuple.Item2;   // Here we set the UI-Thread Culture to the Background Thread

                var longRunningOperationAnswer = LongRunningOperation.DoLongWork(tuple.Item1);
                return longRunningOperationAnswer;

            , new Tuple<IWorkItem, CultureInfo>(workItem, Thread.CurrentThread.CurrentUICulture));  // here we pass the UI-Thread Culture to the State Object



            /* =======================================================================
            *   Handle OnlyOnRanToCompletion Task and process longRunningOperationAnswer back in UiThread
            * =======================================================================*/
            task.ContinueWith((t) =>
            
                IsBusy = false;
                // handle longRunningOperationAnswer here in t.Result
                Log.Debug("Operation completet with 0", t.Result);

            , CancellationToken.None
            , TaskContinuationOptions.OnlyOnRanToCompletion
            , TaskScheduler.FromCurrentSynchronizationContext());

            /* =======================================================================
         *   Handle OnlyOnFaulted Task back in UiThread
         * =======================================================================*/
            task.ContinueWith((t) =>
            
                IsBusy = false;
                AggregateException aggEx = t.Exception;

                if (aggEx != null)
                
                    aggEx.Flatten();
                    Log.ErrorFormat("The Task exited with Exception(s) \n0", aggEx);
                    foreach (Exception ex in aggEx.InnerExceptions)
                    
                        if (ex is SpecialExaption)
                        
                            //Handle Ex here
                            return;
                        
                        if (ex is CustomExeption)
                        
                            //Handle Ex here
                            return;
                        
                    
                
            , CancellationToken.None
            , TaskContinuationOptions.OnlyOnFaulted
            , TaskScheduler.FromCurrentSynchronizationContext());
        

我需要通过uiculture来解析正确的translation.resx进行翻译

【讨论】:

以上是关于在 .Net 中:将 CurrentCulture 保持在新线程上的最佳方式?的主要内容,如果未能解决你的问题,请参考以下文章

.NET 中 CultureInfo 的 CurrentCulture 和 CurrentUICulture 属性有啥区别?

是否可以从 Thread.CurrentThread.CurrentCulture 获取 CSV 分隔符? (。网)

MVC 控制器接收从 CurrentThread.CurrentCulture 解析为不同文化的 DateTime

利用CurrentCulture将人民币符号更改为欧元符号

CultureInfo.CurrentCulture 不包含正确的值 --> numberformat 问题

WPF XAML 绑定和 CurrentCulture 显示