线程、CultureInfo .net、TPL、PLINQ

发布于 2024-10-06 12:11:34 字数 345 浏览 8 评论 0原文

无法将整个 .net 应用程序设置为除 .net 中的用户配置文件区域性之外的其他区域性。控制cultureinfo的适当方法似乎是在DateTime等对象上使用专用方法。

但是,当处理大量遗留代码(并非所有代码都在您的控制之下)时,这是不可能实现的。因此,例如,可以为 Thread 或 Threadpool 创建一个子类/包装器,并在执行委托之前设置所需的文化信息,或者可能需要委托本身包含一组区域性。 (很难验证并且容易出错...)

看看 TPL,更具体地说是 PLINQ,但是我发现很难(如果不是不可能的话)以集中方式更改区域性设置。

有什么建议可以处理遗留代码中的重写线程/应用程序文化信息吗?

谢谢!

It is not possible to set an entire .net application to another culture other than the user profile-culture in .net. The appropriate way to control cultureinfo seems to be using the dedicated methods on objects such as DateTime.

However, when dealing with massive amounts of legacy code (not all code under your control) this is not possible to achieve. Therefor one can for example create a subclass/wrapper to Thread och Threadpool and set the required cultureinfo before the delegate is executed, or the delegate itself could be required to contain a set of the culture. (hard to validate and prone to misstakes...)

Looking at TPL, more specifically PLINQ, however I find it hard, if not impossible, to change culture settings in a centralized way.

Any suggestions that deals withoverriding thread/application-cultureinfo in legacy code?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

倾听心声的旋律 2024-10-13 12:11:34

当线程启动时,其区域性最初是通过使用 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(); 

When a thread is started, its culture is initially determined by using GetUserDefaultLCID from the Windows API. I found no way (I assume there is no way) to override this behavior. Only thing you can do is to set the thread culture afterwards.

I wrote an extension. For that:

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;
        }
    }
} 

So if you use it just after splitting up the original source using .AsParallel() you will get what you want.

    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(); 
狼亦尘 2024-10-13 12:11:34

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

Starting with .NET 4.5 you will be able to define the culture for an entire AppDomain (see paragraph titled "Core New Features and Improvements").

满栀 2024-10-13 12:11:34

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

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

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

我只需要扩展 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

Suprisingly this extension did not slow down my PLINQ queries - that I could measure.

In a complex query with many AsParallel() calls, you might have to call SetCulture() after each AsParallel().
I'm not sure if there is one spot to add .SetCulture() (or one spot for AsParallel for that matter), so I just added .SetCulture() after each AsParallel() call, and that worked great.

Additionally you may consider setting CurrentUICulture as well.
e.g. Using PLINQ to search a Business Object collection to find Business Objects with broken rules (CSLA framework, Broken Rules collection) will cause the PLINQ threads (Thread Pool threads) to lookup localized (our requirement) string resources to set the error string (RuleArgs.Description).

I just needed to extend the ParallelQueryCultureExtensions extension.
This worked well for me (I have to use VB.NET, hence ...):

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
☆獨立☆ 2024-10-13 12:11:34

当我使用 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 / 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());
    }

When i create a Task using the TPL i pass the Culture from the Current UI-Thread to the Background Thread using a State Object.

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());
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文