Threading, CultureInfo .net, TPL, PLINQ - PullRequest
       47

Threading, CultureInfo .net, TPL, PLINQ

6 голосов
/ 09 декабря 2010

Невозможно установить для целого приложения .net другую культуру, отличную от культуры профиля пользователя в .net. Кажется, что подходящим способом управления cultureinfo является использование специальных методов для таких объектов, как DateTime.

Однако при работе с огромным количеством унаследованного кода (не всем кодом, находящимся под вашим контролем) этого достичь невозможно. Для этого можно, например, создать подкласс / оболочку для Thread или Threadpool и установить требуемую информацию Cultureinfo перед выполнением делегата, или сам делегат может потребовать, чтобы он содержал набор культуры. (трудно проверить и склонен к ошибкам ...)

Глядя на TPL, а точнее на PLINQ, мне трудно, если не невозможно, централизованно изменять настройки культуры.

Есть ли какие-либо предложения, касающиеся использования потока / application-cultureinfo в устаревшем коде?

Спасибо!

Ответы [ 4 ]

6 голосов
/ 05 августа 2011

Когда поток запускается, его культура первоначально определяется с помощью GetUserDefaultLCID из Windows API.Я не нашел способа (я предполагаю, что нет никакого способа) переопределить это поведение.Единственное, что вы можете сделать - это установить культуру потоков позже.

Я написал расширение.Для этого:

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 (), вы получите то, что вы хотите.

2 голосов
/ 24 сентября 2011

Начиная с .NET 4.5 , вы сможете определить культуру всего домена приложений (см. Параграф «Основные новые функции и улучшения»).

1 голос
/ 19 августа 2011

Удивительно, но это расширение не замедлило мои запросы PLINQ - что я мог измерить.

В сложном запросе со многими вызовами AsParallel () вам, возможно, придется вызывать SetCulture () после каждого AsParallel (). Я не уверен, есть ли одна точка для добавления .SetCulture () (или одна точка для AsParallel в этом отношении), поэтому я просто добавил .SetCulture () после каждого вызова AsParallel (), и это прекрасно работало.

Кроме того, вы можете также рассмотреть настройку 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
0 голосов
/ 27 января 2012

Когда я создаю Задание с использованием 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());
    }
...