Threading, CultureInfo .net , TPL, PLINQ

6
在 .net 中,无法将整个应用程序设置为与用户配置文件区域设置不同的区域设置。控制 cultureinfo 的适当方法似乎是使用 DateTime 等对象上的专用方法。
然而,在处理大量遗留代码(并非所有代码都在您的控制范围内)时,这是不可能实现的。因此,例如可以创建一个 Thread 或 Threadpool 的子类/包装器,并在执行委托之前设置所需的 cultureinfo,或者要求委托本身包含一组 culture。 (难以验证且容易出错...)
但是,在查看 TPL,更具体地说是 PLINQ 时,我发现很难甚至不可能以集中方式更改文化设置。
有没有任何建议可以处理遗留代码中覆盖线程/应用程序-cultureinfo 的问题?
谢谢!
4个回答

6

当一个线程启动时,它的文化最初是通过使用Windows API中的GetUserDefaultLCID确定的。我没有找到任何方法(我假设没有方法)可以覆盖此行为。您唯一能做的就是在之后设置线程文化。

我写了一个扩展程序。为此:

public static class ParallelQueryCultureExtensions
{
    public static ParallelQuery<TSource> SetCulture<TSource>(this ParallelQuery<TSource> source, CultureInfo cultureInfo)
    {
        SetCulture(cultureInfo);
        return source
            .Select(
                item =>
                    {
                        SetCulture(cultureInfo);
                        return item;
                    });
    }

    private static void SetCulture(CultureInfo cultureInfo) {
        if (Thread.CurrentThread.CurrentCulture != cultureInfo) {
            Thread.CurrentThread.CurrentCulture = cultureInfo;
        }
    }
} 

如果您在使用.AsParallel()分割源代码后立即使用它,您将获得您想要的结果。

    CultureInfo kaCulture = CultureInfo.GetCultureInfo("ka-Ge");

    int[] array = new int[100];
    Random random =  new Random();
    int index =0;
    Array.ForEach(array, i => { array[index++] = index;});

    array
        .AsParallel()
        .SetCulture(kaCulture)
        .ForAll(
            i =>
                {
                    Thread.Sleep(random.Next(5));
                    Console.WriteLine("Thread-{0} \t Culture-'{1}' \t Element-{2}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.CurrentCulture, i);
                });

    Console.WriteLine("Press any key to quit");
    Console.ReadKey(); 

1
这个完美地运行了,至少在我能够转移到 .net 4.5之前是这样的 :) - Porco

2

.NET 4.5开始,您将能够为整个AppDomain定义文化(请参见标题为“核心新功能和改进”的段落)。


1

令人惊讶的是,这个扩展并没有减慢我的PLINQ查询速度 - 我可以测量。

在一个包含多个AsParallel()调用的复杂查询中,您可能需要在每个AsParallel()之后调用SetCulture()。 我不确定是否有一个地方可以添加.SetCulture()(或者说AsParallel的一个地方),所以我只是在每个AsParallel()调用之后添加了.SetCulture(),效果很好。

此外,您还可以考虑设置CurrentUICulture。 例如,使用PLINQ搜索业务对象集合以查找具有损坏规则(CSLA框架,Broken Rules集合)的业务对象将导致PLINQ线程(线程池线程)查找本地化(我们的要求)字符串资源以设置错误字符串(RuleArgs.Description)。

我只需要扩展ParallelQueryCultureExtensions扩展即可。 这对我很有效(我必须使用VB.NET,因此...):

Public Module PLINQExtensions

    <Extension()> _
    Public Function SetCulture(Of TSource)(ByVal source As ParallelQuery(Of TSource), ByVal culture As CultureInfo, ByVal uiCulture As CultureInfo) As ParallelQuery(Of TSource)
        SetCulture(culture, uiCulture)
        Return source.Select(Function(item)
                                 SetCulture(culture, uiCulture)
                                 Return item
                             End Function
                            )
    End Function

    <Extension()> _
    Private Sub SetCulture(ByVal culture As CultureInfo, ByVal uiCulture As CultureInfo)
        If (Not Thread.CurrentThread.CurrentCulture.Equals(culture)) Then
            Thread.CurrentThread.CurrentCulture = culture
        End If

        If (Not Thread.CurrentThread.CurrentUICulture.Equals(uiCulture)) Then
            Thread.CurrentThread.CurrentUICulture = uiCulture
        End If
    End Sub

End Module

0

当我使用TPL创建任务时,我通过状态对象将当前UI线程的Culture传递到后台线程。

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 / Multila`enter code here`nguate 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());
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接