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

时间:2011-02-28 16:00:02

标签: .net wpf multithreading c#-4.0 culture

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

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

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

    context.CurrentCulture = Thread.CurrentThread.CurrentCulture;
    context.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
    
  2. 在新主题上,从保存的上下文

    初始化
    Thread.CurrentThread.CurrentCulture = context.CurrentCulture;
    Thread.CurrentThread.CurrentUICulture = context.CurrentUICulture;
    
  3. 但是在这个TPL时代,异步编程和lambda代理,感觉不对。

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

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

4 个答案:

答案 0 :(得分:25)

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

对文化进行临时线程局部更改很好,试图将“过程”切换到另一种文化是一个你将要寻找几个月的bug的来源。字符串整理顺序是最棘手的问题来源。


编辑:.NET 4.5中使用CultureInfo.DefaultThreadCurrentCulture和DefaultThreadCurrentUICulture属性修复了此问题。


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

答案 1 :(得分:5)

我不明白为什么Passant先生会对此发出警告,并同时表示对文化进行“临时”“线程本地化”变更是很好的。没有线程文化是真正的线程本地 - 它可以通过公共属性引用线程的任何东西,正如我们所见。如果可以在短时间内更改它,那么为什么不能长时间更改它呢?你在哪里越线,为什么?

我也不是真的理解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 }; }
}

答案 2 :(得分:3)

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

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

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

(是我,还是微软使用全球化和本地化这个词,相当容易混淆,互换?)

答案 3 :(得分:2)

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

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) \n{0}", 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进行翻译