在Parallel.ForEach内部使用锁定的正确方法是哪种?

时间:2019-01-15 12:55:57

标签: c# .net multithreading locking parallel.foreach

我想学习在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 :(得分:2)

如果您的应用程序是并发的(并行性是并发类型之一),并且您想使用线程安全的集合,则没有理由自己锁定集合。 Microsoft提供的一堆并发集合存在于System.Collections.Concurrent中 Thread-safe Collections

答案 1 :(得分:2)

Parallel.ForEach是一种尝试使代码具有更多并行性的方法。 lock倾向于在代码 1 减少并行性。因此,要合并它们 2

Olegl suggested一样,并发回收可能是避免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,因为它在 less 起作用的同时持有其他所有工人都在激烈争夺的锁。


1 除非您知道将被请求的所有锁都具有足够的细粒度,以至没有两个并行线程会尝试获取同一锁。但是如果我们知道,为什么还要再次使用锁?

2 因此,我将四肢伸张地说,当所有并行锁都在同一锁对象上时,将它们组合在一起总是“不正确的”,除非 lock之外并行进行着大量处理。

答案 2 :(得分:0)

例如这种用法:

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;
    }