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

在.Net中:在新线程上保持CurrentCulture的最佳方法?,.net,wpf,multithreading,c#-4.0,culture,.net,Wpf,Multithreading,C# 4.0,Culture,在.NET4.0WPF项目中,我们需要在每个线程上保持与主线程相同的CurrentCulture 在给定条件下,我们可以使用如下代码初始化新线程的区域性: 将信息保存在变量(上下文)中 在新线程上,从保存的上下文初始化 Thread.CurrentThread.CurrentCulture = context.CurrentCulture; Thread.CurrentThread.CurrentUICulture = context.CurrentUICulture; 但在这个TPL、异

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

在给定条件下,我们可以使用如下代码初始化新线程的区域性:

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

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

    Thread.CurrentThread.CurrentCulture = context.CurrentCulture;
    Thread.CurrentThread.CurrentUICulture = context.CurrentUICulture;
    
  • 但在这个TPL、异步编程和lambda委托的时代,感觉不太对劲

    是的,我们实际上可以在应用程序运行时更改区域性,但这是另一回事


    您知道我们应该初始化哪些设置、属性或配置以保持跟踪吗?

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

    对区域性进行临时线程本地更改是可以的,尝试将“进程”切换到另一个区域性会导致几个月的bug。字符串排序顺序是最糟糕的问题来源


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



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

    我不明白为什么帕桑先生对此提出警告,同时说对文化进行“临时”“线程本地”更改是可以的。没有线程区域性是真正的线程本地的——正如我们所看到的,任何可以通过公共属性引用线程的东西都可以使用它。如果可以在短时间内改变它,那么为什么不能在更长时间内改变它呢?你在哪里越界,为什么

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

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

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


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

    当我使用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) \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());
            }
    
    private void WorkProcessingAsync(IWorkItem workItem)
    {
    IsBusy=true;
    /* =============================
    *创建TPL任务并在状态对象中传递当前UiCulture,以解析后台线程中翻译/全球化/多语言功能的正确.resx文件
    * ==============================*/
    Task Task=Task.Factory.StartNew((stateObj)=>
    {
    //这里我们已经在任务后台线程中了
    //保存并强制转换给定的stateObj
    var tuple=stateObj作为tuple;
    Assert(tuple!=null,“tuple!=null”);
    Thread.CurrentThread.CurrentUICulture=tuple.Item2;//这里我们将UI线程区域性设置为后台线程
    var longRunningOperationAnswer=LongRunningOperation.DoLongWork(tuple.Item1);
    返回长运行操作应答器;
    },新元组(workItem,Thread.CurrentThread.CurrentUICulture));//这里我们将UI线程区域性传递给状态对象
    /* =======================================================================
    *仅处理OnArntoCompletion任务,并在UiThread中处理longRunningOperationAnswer
    * =======================================================================*/
    任务。继续((t)=>
    {
    IsBusy=false;
    //在t.结果中处理长时间运行操作回答
    Log.Debug(“使用{0}完成操作”,t.Result);
    },CancellationToken。无
    ,TaskContinuationOptions.OnlyOnRanToCompletion
    ,TaskScheduler.FromCurrentSynchronizationContext());
    /* =======================================================================
    *仅在UiThread中处理返回的出错任务
    * =======================================================================*/
    任务。继续((t)=>
    {
    IsBusy=false;
    AggregateException aggEx=t.异常;
    if(aggEx!=null)
    {
    aggEx.flatte();
    ErrorFormat(“任务退出时出现异常)\n{0}”,aggEx);
    foreach(aggEx.InnerExceptions中的异常)
    {
    如果(ex为特殊许可证)
    {
    //在这里处理
    返回;
    }
    如果(ex是CustomException)
    {
    //在这里处理
    
    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());
            }