Parallels.ForEach занимает столько же времени, сколько Foreach - PullRequest
10 голосов
/ 29 ноября 2011

Все,

Я использую 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 с.Когда я запускаю ту же самую вещь в параллельном режиме, это занимает то же самое время и загружает мой ЦП (Intel I5, 2 ​​ядра, 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;
    }

Ответы [ 2 ]

5 голосов
/ 29 ноября 2011

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

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

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

System.Threading.ThreadPool.SetMaxThreads(4, 4);
0 голосов
/ 29 ноября 2011

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

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

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

Может быть, я старая головка для пакетной обработки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...