Как правильно использовать блокировку внутри Parallel.ForEach? - PullRequest
0 голосов
/ 15 января 2019

Я хотел бы узнать, как лучше использовать блокировку в Parallel.ForEach. Должен ли я заблокировать весь блок кода внутри итерации или я должен блокировать только тот объект, который я хочу использовать как многопоточный, до того, как я выполню какой-либо процесс?

например:

Parallel.ForEach(list, item =>
        {
            lock (secondList)
            {
                //consider other processes works in here

                if (item.Active)
                    secondList.Add(item);
            }
        });

или

Parallel.ForEach(list, item =>
        {
            //consider other processes works in here

            if (item.Active)
            {
                lock (secondList)
                    secondList.Add(item);
            }
        });

Ответы [ 3 ]

0 голосов
/ 15 января 2019

Parallel.ForEach - это способ увеличить параллелизм в вашем коде. lock стремится к снижению параллелизма в вашем коде 1 . Таким образом, редко бывает правильным объединить их 2 .

Как Олегл предложил , параллельные коллекции могут быть одним из способов, чтобы избежать здесь lock.

Другим интересным подходом было бы использовать PLINQ здесь вместо Parallel.ForEach. Уже 2019 год, что интересного в написании очередного цикла?

Это могло бы сделать что-то вроде этого:

secondList.AddRange(list.AsParallel.Where(item =>
{
    //consider other processes works in here

    return item.Active;
});

Это позволяет вам сохранить не-поточно-безопасную коллекцию secondList, но при этом не беспокоиться о блокировках - потому что это ваш собственный существующий поток, вызывающий AddRange, который в итоге потребляет IEnumerable<T>, который предлагает PLINQ; так что только один поток добавляет элементы в коллекцию.

PLINQ пытается настроить параметры буферизации, но может не выполнить достаточно хорошую работу, в зависимости от размера ввода list и количества потоков, которые он выберет для использования. Если вы недовольны каким-либо ускорением (или не достигли его), попробуйте поиграть с методами WithXxx, которые он предлагает, прежде чем списывать его.


Если бы у меня было , чтобы выбрать между вашими двумя примерами (при условии, что они оба верны), я бы выбрал вариант 2, потому что он работает на меньше , удерживая замок, который горячо оспаривается всеми другими работниками.


1 Если вы не знаете, что все запрошенные блокировки достаточно тонки, чтобы никакие два параллельных потока не пытались получить одну и ту же блокировку. Но если мы знаем это, почему мы снова используем замки?

2 И поэтому я выхожу на конечность и говорю, что «всегда» неправильно комбинировать их, когда все параллельные блокировки находятся на одном и том же объекте блокировки, , если не будет значительным обработка происходит параллельно вне lock.

0 голосов
/ 15 января 2019

Например, такого рода употребления:

public static T CastTo<T>(this ArrayOfKeyValueOfstringstringKeyValueOfstringstring[] item)
    {
        var obj = Activator.CreateInstance(typeof(T), true);
        var padlock = new object();

        Parallel.ForEach(typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public), prop =>
        {
            lock (padlock)
            {
                if (!prop.TryGetAttribute<GldnhrnFieldAttribute>(out var fieldAttribute))
                    return;

                var code = fieldAttribute?.Code;

                if (string.IsNullOrEmpty(code)) return;

                SetPropertyValue(item, obj, prop);
            }
        });

        return (T)obj;
    }

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

 public static T CastTo<T>(this ArrayOfKeyValueOfstringstringKeyValueOfstringstring[] item)
    {
        var obj = Activator.CreateInstance(typeof(T), true);
        var padlock = new object();

        Parallel.ForEach(typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public), prop =>
        {
            if (!prop.TryGetAttribute<GldnhrnFieldAttribute>(out var fieldAttribute))
                return;

            var code = fieldAttribute?.Code;

            if (string.IsNullOrEmpty(code)) return;

            lock (padlock)
                SetPropertyValue(item, obj, prop);
        });

        return (T)obj;
    }
0 голосов
/ 15 января 2019

Если ваше приложение работает одновременно (параллелизм - это один из типов параллелизма) и вы хотите использовать потокобезопасную коллекцию, нет причин блокировать коллекции самостоятельно. Существует множество одновременных коллекций, предоставляемых Microsoft, которые существуют в System.Collections.Concurrent. Потокобезопасные коллекции

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