views:

121

answers:

3

We had a little discussion in the office, and got no documented answer:

Is System.Array.SetValue thread safe?

using System;
using System.Text;
using System.Threading;

namespace MyApp
{
    class Program
    {
        private static readonly object[] arr = new object[3];

        static void Main(string[] args)
        {
            string value1 = "hello";
            int value2 = 123;
            StringBuilder value3 = new StringBuilder();
            value3.Append("this"); 
            value3.Append(" is "); 
            value3.Append("from the StringBuilder");

            var states = new object[]
                             {
                                 new object[] {0, value1},
                                 new object[] {1, value2},
                                 new object[] {2, value3}
                             };

            ThreadPool.QueueUserWorkItem(MySetValue, states[0]);
            ThreadPool.QueueUserWorkItem(MySetValue, states[1]);
            ThreadPool.QueueUserWorkItem(MySetValue, states[2]);
            Thread.Sleep(0);

            Console.WriteLine("press enter to continue");
            Console.ReadLine();

            // print the result
            Console.WriteLine("result:");
            for (int i = 0; i < arr.Length; i++)
            {
                Console.WriteLine("arr[{0}] = {1}", i, arr[i]);
            }

            // quit
            Console.WriteLine("press enter to quit");
            Console.ReadLine();

        }

        // callback
        private static void MySetValue(object state)
        {
            var args = (object[]) state;
            var index = (int)args[0];
            var value = args[1];
            arr[index] = value; // THREAD-SAFE ??
        }
    }
}

As you can see, every thread sets a different, unique item in the static array. I looked deep into the code using reflector (and looked into mscorlib.pdb). Eventually there's a call to:

[MethodImplAttribute(MethodImplOptions.InternalCall)]
private unsafe extern static void InternalSetValue(void * target, Object value);

Which is not documented. Take a look at MSDN's documentation to System.Array in general, and to SetValue(object, int) in particular.. Nothing about thread-safety (or maybe I'm missing something).

As it's expressed by Jon Skeet's answer to a similar question:

I believe that if each thread only works on a separate part of the array, all will be well

I'm trying to get a definite answer to GetValue(int) and SetValue(object, int) regarding this issue. Does someone have a documentation link and/or a better understanding of InternalSetValue?

A: 

In your example where each thread sets a different item in the array it will be thread safe. See it as a fridge, there are three cans of beer in there, three different go to the fridge and pick his can of beer, all will go well, they will drink it half-way, put it back and return for it later.

However you can't share 1 can of beer with three people, neither when programming or in real-life.

so yeah:

if each thread only works on a separate part of the array, all will be well

PS: I have no source to verify this though, but happen to use threads all the time and I never had a starvation(?) problem when each thread reads 'n writes simultaneously in an array. I DO suffer from problems when sharing the 'same can of beer' therefore I believe my answer is correct, but I'd like to see someone verify this.

Pieter888
How can you tell? Maybe the `System.Array` class generates its own cache on every setter invocation, and this process is not thread-safe?
Ron Klein
+1  A: 

MSDN: Array class

Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

This implementation does not provide a synchronized (thread safe) wrapper for an Array; however, .NET Framework classes based on Array provide their own synchronized version of the collection using the SyncRoot property.

It's not thread safe!

Edit:

Some extra info, the method SetValue on the Array class is not called in normal circumstances, it's only called when the array is used through the IList interface.

the following code:

int[] arr = ...
arr[i] = value;

Won't generate a call to SetValue(), instead a OpCodes.Stelem opcode will be generated instead.

So it's rather irrelevant if the SetValue method is thread safe or not unless the array is accessed using a IList reference.

Pop Catalin
I really don't know how I missed that. Thanks!
Ron Klein
So it's not thread-safe in general, but how about the specific scenario with each thread accessing only a separate part of the array?
0xA3
@divo, its not guaranteed to be thread safe, meaning even if current implementation is that's an implementation detail.
Pop Catalin
A: 

In your example, the calls to InternalSetValue(void *, object) are being made to three different memory locations. Therefore, it should be thread-safe. The writes to those locations are not going bleed over into other locations, even if they are members of the same array.

Jeffrey L Whitledge
Indeed, the code example invokes `InternalSetValue` three times with tree different memory locations, but that's as far as I can tell. I don't know what's the implementation of `InternalSetValue`, and therefore I cannot be sure about its thread-safety.
Ron Klein