3

是否需要在使用多个线程更新共享数组时锁定此功能?前提是每个 Arrays 元素最多只能由 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;   
            }

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

4

1 回答 1

0

锁定可能是不必要的,但这种方法迫使您考虑可见性、易变性、内存模型以及所有这些棘手的东西。当您编写应用程序代码以易于维护并保证在它可能运行的任何 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在末尾添加 a 来增加当前方法的稳健性。从技术上讲,它可能不是必需的,但它可以以非常小的价格让您高枕无忧!

同步内存访问如下:执行当前线程的处理器不能以这样一种方式重新排序指令,即在调用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;
}
于 2020-06-02T01:09:46.893 回答