【问题标题】:Locking under exclusive threads constraint在独占线程约束下锁定
【发布时间】:2020-06-01 06:41:19
【问题描述】:

是否需要在使用多个线程更新共享数组时锁定此功能?前提是每个数组元素最多只能由 1 个线程访问(读取和写入)。

谢谢。

例如

       public string[] UpdateArray
        (List<string> myList, Func<string,string>del)
        {
         int count = myList.Count;
         string[] myArray = new string[count];
         Parallel.For(0, count, i => myArray[i] = del(myList[i]));
         return myArray;
       }

更新:

它的预期用途如下,在 WPF 同步上下文中


        public Task<string[]> updateArrayTask()
            {
                List<string> myList = GetMyList();
                Func<string, string> del = MyDel;
                var t1=Task<string[]>.Run(() => UpdateArray(myList, del));
                return t1;   
            }

然后在异步函数中等待这个任务。

【问题讨论】:

标签: c# multithreading parallel-processing thread-safety


【解决方案1】:

锁定可能是不必要的,但这种方法迫使您考虑可见性、易变性、内存模型和all this thorny stuff。当您编写应用程序代码以易于维护并保证在它可能运行的任何 CPU 架构上产生正确的结果时,这是非常不可取的。所以我的建议是通过使用PLINQ 而不是Parallel 类来绕过这个问题:

public string[] UpdateArray(List<string> myList, Func<string, string> del)
{
    return myList
        .AsParallel()
        .AsOrdered()
        .Select(s => del(s))
        .ToArray();
}

更新:如果您发现 PLINQ 方法的开销太大,您可以通过在末尾添加 Thread.MemoryBarrier 来增加当前方法的稳健性。从技术上讲,它可能不是必需的,但它可以以非常小的价格让您高枕无忧!

按如下方式同步内存访问:执行当前线程的处理器不能以这样的方式重新排序指令,即调用MemoryBarrier()之前的内存访问在调用MemoryBarrier()之后的内存访问之后执行。

要进一步减少并行性的开销,您可以使用Partitioner.Create 方法,这样您就可以使用列表的大块部分而不是单个元素。

public string[] UpdateArray(List<string> myList, Func<string, string> del)
{
    string[] myArray = new string[myList.Count];
    Parallel.ForEach(Partitioner.Create(0, myList.Count), range =>
    {
        for (int i = range.Item1; i < range.Item2; i++)
        {
            myArray[i] = del(myList[i]);
        }
    });
    Thread.MemoryBarrier();
    return myArray;
}

【讨论】:

  • Parallel.For 不是 PLINQ 的一部分吗?
  • @Enigmativity 没有。 AFAIK Parallel.ForTask Parallel Library (TPL) 的一部分。 PLINQ 是 LINQ 的扩展,它允许通过使用 AsParallel 运算符启动查询来并行执行查询。我不确定 PLINQ 是否被视为 TPL 的一部分。
  • @西奥多·祖利亚斯。使用 Parallel 方法的重点是在不影响性能的情况下保持顺序,您是否担心 myList 同时被另一个线程修改?据我所知,使用 PLINQ 时这个问题仍然存在。
  • @西奥多·祖利亚斯。 PLINQ 本身不能修改 myList AFAIK,但另一个线程可以,或者您的意思是 PLINQ 可以使用本地副本?
  • @pablo 我通过添加 AsOrdered 运算符更正了我的答案。您的方法的问题不在于myList 的枚举。是存储在myArray 变量中的值的可见性。从理论上讲,调用UpdateArray 的线程可能看不到数组的最终状态,因为它的缓存部分可能仍驻留在运行该线程的内核的本地缓存中。我相信Parallel.For 可能会在完成时设置一个内存屏障,使这种情况变得不可能,但我不会赌上我的生命。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2013-07-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多