Threading, CultureInfo .net , TPL, PLINQ
Asked Answered
R

4

6

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!

Raid answered 9/12, 2010 at 14:22 Comment(0)
R
6

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(); 
Rod answered 4/8, 2011 at 21:37 Comment(1)
This worked perfect, at least until I can move to .net 4.5 :)Tinny
H
2

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").

Hydrolyte answered 24/9, 2011 at 10:18 Comment(0)
C
1

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
Chaker answered 19/8, 2011 at 16:21 Comment(0)
B
0

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());
    }
Bochum answered 27/1, 2012 at 9:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.