Цикл Foreach и переменные с блокировкой - PullRequest
2 голосов
/ 19 июля 2011

В основном я хочу сделать этот псевдо-код

List<DatabaseRecord> records;

List<ChangedItem> changedItems;

Parallel.ForEach<DatabaseRecord>(records, (item, loopState) =>
{
    if (item.HasChanged)
    {
        lock (changedItems)
        {
            changedItems.Add(new ChangedItem(item));
        }
    }
});

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

Ответы [ 4 ]

9 голосов
/ 19 июля 2011

Почему бы вам не использовать PLinq? Блокировка не требуется:

changedItems = records.AsParallel()
                      .Where(x => x.HasChanged)
                      .Select(x => new ChangedItem(x))
                      .ToList();

Поскольку вы проецируете в новый список ChangedItem и у вас нет никаких побочных эффектов, это будет путь, по моему мнению.

1 голос
/ 19 июля 2011

Не могли бы вы использовать ConcurrentCollection для ваших измененных элементов. Что-то вроде ConcurrentQueue? Тогда вам не нужно будет блокировать вообще.

Обновление:

Что касается ConcurrentQueue, Enqueue не будет блокировать поток для обеспечения безопасности потока операций. Он остается в режиме пользователя с SpinWait ...

public void Enqueue(T item)
{
    SpinWait wait = new SpinWait();
    while (!this.m_tail.TryAppend(item, ref this.m_tail))
    {
        wait.SpinOnce();
    }
}
1 голос
/ 19 июля 2011

Кажется, вы используете этот код в одном потоке? если это один поток, блокировка не требуется. Работает ли это нормально, когда вы удаляете строку «lock (changeItems)» Код, опубликованный @BrokenGlass, более понятен и прост для понимания.

1 голос
/ 19 июля 2011

Я не думаю, что блокировка списка вызовет сериализацию / десериализацию списка, поскольку блокировка происходит в закрытом поле, доступном для всех объектов (syncBlockIndex).Однако рекомендуемый способ блокирования - это создание личного поля, которое вы будете использовать для блокировки:

object _lock = new object();

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

Что касается PLinq, я думаю, что решение использовать его зависит отна что похож ваш хост и загрузка.Например, в ASP.NET PLINQ более требователен к процессору , что позволит сделать это быстрее, но за счет отказа от обработки других веб-запросов.Синтаксис, по общему признанию, намного чище.

...