Locking under exclusive threads constraint

Viewed 92

Is necessary to lock this function, when updating the shared Array using many threads?, providing that each Arrays element is accessed (read and written), by only 1 thread at most.

Thank you.

e.g.

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

update:

its intended use would be as follows,in a WPF sync context


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

and then await this Task in an async function.

1 Answers

It is probably unnessesary to lock, but this approach forces you to think about visibility, volatility, memory models, and all this thorny stuff. Which is quite undesirable when you are writing application code with the intention of being easy to maintain, and guaranteed to produce correct results on any CPU architecture it may run. So my suggestion is to bypass the problem by using PLINQ instead of the Parallel class:

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

Update: If you find that the PLINQ approach has too much overhead, you could increase the robustness of your current approach by adding a Thread.MemoryBarrier at the end. Technically it may not be required, but it can buy you peace of mind for a very small price!

Synchronizes memory access as follows: The processor executing the current thread cannot reorder instructions in such a way that memory accesses prior to the call to MemoryBarrier() execute after memory accesses that follow the call to MemoryBarrier().

To reduce further the overhead of parallelism you could use the Partitioner.Create method, so that you can work with chunky portions of the list instead of its individual elements.

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