Parallels.ForEach Принимая то же время, что и Foreach

Все,

Я использую Parallels.ForEach следующим образом

private void fillEventDifferencesParallels(IProducerConsumerCollection<IEvent> events, Dictionary<string, IEvent> originalEvents)
    {
        Parallel.ForEach<IEvent>(events, evt =>
        {
            IEvent originalEventInfo = originalEvents[evt.EventID];
            evt.FillDifferences(originalEventInfo);
        });
    }

Хорошо, поэтому проблема у меня есть, у меня есть список из 28 из них (тестовый образец, который должен иметь возможность масштабирования до 200+), а метод FillDifferences довольно трудоемкий (около 4 с за звонок), Таким образом, среднее время для запуска в нормальном ForEach составляет около 100-130 секунд. Когда я запускаю одно и то же в Parallel, это занимает столько же времени, сколько и копейки моего процессора (Intel I5, 2 ​​Core, 2 потока на ядро), что приводит к тому, что приложение становится вялым во время выполнения этого запроса (это работает в потоке который был порожден потоком GUI).

Итак, мой вопрос: что я делаю неправильно, что приводит к тому, что это займет столько же времени? Я читал, что List не был потокобезопасным, поэтому я переписал его, чтобы использовать IProducerConsumerCollection. Есть ли другие подводные камни, которые могут быть причиной этого?

Метод FillDifferences вызывает статический класс, который использует отражение, чтобы узнать, сколько различий между оригиналом и измененным объектом. Статический объект не имеет глобальных переменных, только локальных для вызываемых методов.

Некоторые из вас хотели узнать, что вызвал метод FillDifferences(). Вот где это в конечном итоге:

  public  List<IDifferences> ShallowCompare(object orig, object changed, string currentName)
    {
        List<IDifferences> differences = new List<IDifferences>();
        foreach (MemberInfo m in orig.GetType().GetMembers())
        {
            List<IDifferences> temp = null;

            //Go through all MemberInfos until you find one that is a Property.
            if (m.MemberType == MemberTypes.Property)
            {
                PropertyInfo p = (PropertyInfo)m;
                string newCurrentName = "";
                if (currentName != null && currentName.Length > 0)
                {
                    newCurrentName = currentName + ".";
                }
                newCurrentName += p.Name;
                object propertyOrig = null;
                object propertyChanged = null;

                //Find the property Information from the orig object
                if (orig != null)
                {
                    propertyOrig = p.GetValue(orig, null);
                }

                //Find the property Information from the changed object
                if (changed != null)
                {
                    propertyChanged = p.GetValue(changed, null);
                }

                //Send the property to find the differences, if any. This is a SHALLOW compare.
                temp = objectComparator(p, propertyOrig, propertyChanged, true, newCurrentName);
            }
            if (temp != null && temp.Count > 0)
            {
                foreach (IDifferences difference in temp)
                {
                    addDifferenceToList(differences, difference);
                }
            }
        }
        return differences;
    }

Ответ 1

Я считаю, что вы можете столкнуться с затратами на переключение контекста потока. Поскольку эти задачи выполняются долгое время, я могу представить, что на ThreadPool создается множество потоков для их обработки.

  • 0ms == 1 thread
  • 500ms == 2 потока
  • 1000 мс == 3 потока
  • 1500 мс == 4 потока
  • 2000 мс == 5 потоков
  • 2500 мс == 6 потоков
  • 3000 мс == 7 потоков
  • 3500 мс == 8 потоков
  • 4000 мс == 9 потоков

К 4000 мс только первая задача была завершена, поэтому этот процесс продолжится. Возможное решение заключается в следующем.

System.Threading.ThreadPool.SetMaxThreads(4, 4);

Ответ 2

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

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

Если это правда, у меня возникнет соблазн перекодировать код. У вас есть два потока для поиска свойств для сравнения и один для сравнения их и общей очереди. Может быть, еще один, чтобы бросить классы в списке и сопоставить результаты.

Может быть, я старую команду обработки партии, хотя.