Я писал код для списка, который анализирует словарь с помощью регулярного выражения и еще много чего для элементов DateTime. Следующий код ДЕЙСТВИТЕЛЬНО работает и работает довольно хорошо. Однако, возвращаясь назад, я замечаю этот Parallel.ForEach, который я написал. Мой вопрос простой; есть ли прирост производительности при использовании Parallel.ForEach, если я блокирую поток? Я чувствую, что это разрушает всю цель.

Кроме того, я чувствую, что та же идея / вопрос применима к асинхронной ситуации, поскольку я блокирую поток.

namespace StackOverflowExample
{
    public class ScheduledTimeList : List<ScheduledTime>
    {
        public static ScheduledTimeList Parse(
            Dictionary<string, string> dateTimeStrings, string startRegex,
            string endRegex, object sync)
        {
            var scheduledTimes = new ScheduledTimeList();

            Parallel.ForEach(from dt in dateTimeStrings.AsParallel()
                             select ParseScheduledTime(dt, startRegex, endRegex), dt =>
            {
                lock (sync) { scheduledTimes.Add(dt); }
            });

            return scheduledTimes;
        }

        private static ScheduledTime ParseScheduledTime(
            KeyValuePair<string, string> dateTime, string startRegex,
            string endRegex)
        {
            var startString =
                dateTime.Key + " " + new Regex(startRegex).Match(dateTime.Value);
            var endString =
                dateTime.Key + " " + new Regex(endRegex).Match(dateTime.Value);

            var startDateTime = DateTime.Parse(startString);
            var endDateTime = DateTime.Parse(endString);

            return new ScheduledTime(startDateTime,
                endDateTime);
        }

        private static async Task<ScheduledTime> ParseScheduledTimeAsync(
            KeyValuePair<string, string> dateTime, string startRegex,
            string endRegex)
        {
            var startString = Task.Run(() =>
                dateTime.Key + " " + new Regex(startRegex).Match(dateTime.Value).Value);
            var endString = Task.Run(() =>
                dateTime.Key + " " + new Regex(endRegex).Match(dateTime.Value).Value);
            await Task.WhenAll(startString, endString);

            var startDateTime = await Task.Run(() =>
                DateTime.Parse(startString.Result));
            var endDateTime = await Task.Run(() =>
                DateTime.Parse(endString.Result));

            return new ScheduledTime(startDateTime,
                endDateTime);
        }
    }
}
0
Steven Floyd 22 Окт 2015 в 05:20

2 ответа

Лучший ответ

Вот некоторые недоразумения, проблемы с анализом и неправильные решения.

Во-первых, если вы запустите что-то вроде Parallel.ForEach, AsParallel или Tasks, это в значительной степени то же самое, все работает поверх TPL, тогда есть не один поток, а много или один, все зависит от балансировки нагрузки, но я хочу сказать, что блокировка не гарантирует, что все части будут работает в том же потоке. Вместо этого блокировка гарантирует, что в любой момент времени внутри заблокированной области будет выполняться только один поток. Конечно, не одно и то же .

Вторая проблема заключается в том, что вы совершили обычную ошибку новичков. Введение слишком большого количества параллелизма не помогает. Конечно, я вижу проблему в методе Parse, когда вы сначала запускаете внутренний параллельный блок, чтобы получить результаты, если подход AsParallel (), PLINQ, там он работает полностью одновременно, но затем вы возвращаете IEnumerable в Parallel.ForEach, заставляя PLINQ синхронизировать результаты обратно в IEnumerable. Это плохое решение для производительности.

И третья ошибка - использовать старые коллекции, такие как List и т. Д., В параллельном коде. Параллельный код лучше всего работает поверх параллельных коллекций. Здесь это будет означать использование ConcurrentBag вместо List.

 public class ScheduledTimeList : ConcurrentBag<ScheduledTime>

В итоге в методе Parse это может выглядеть так:

var scheduledTimes = new ScheduledTimeList();

dateTimeStrings
.AsParallel()
.ForAll(dts =>
{
  var dt = ParseScheduledTime(dts, startRegex, endRegex);
  scheduledTimes.Add(dt);//add's items concurently into bag,
});

Параллельное / асинхронное / параллельное программирование - сложная задача, и она действительно требует серьезного изучения, даже помимо задач, классов Parallel, PLINQ и async / await.

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

2
ipavlu 22 Окт 2015 в 03:37

есть ли прирост производительности при использовании Parallel.ForEach, если я блокирую поток?

Наоборот. Вы несете накладные расходы на параллельную работу, а затем сразу же заставляете все ваши потоки запускаться сериализованным образом, потому что весь блок кода управляется оператором блокировки.

1
Eric J. 22 Окт 2015 в 02:32