System.Array的SetValue / GetValue方法是否是线程安全的?

时间:2009-12-09 15:05:59

标签: c# arrays thread-safety setvalue

我们在办公室进行了一些讨论,没有记录答案:

System.Array.SetValue线程是否安全?

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

如您所见,每个线程在静态数组中设置一个不同的唯一项。我使用反射器深入研究了代码(并查看了mscorlib.pdb)。最后有一个电话:

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

没有记录。一般来看MSDN的文档到System.Array,特别是SetValue(object, int)。没有关于线程安全的信息(或者我可能缺少某些东西)。

正如Jon Skeet对similar question的回答所表达的那样:

  

我相信如果每个线程只   适用于阵列的单独部分,   一切都会好的

我正试图就此问题得到GetValue(int)SetValue(object, int)的明确答复。是否有人有文档链接和/或更好地了解InternalSetValue

3 个答案:

答案 0 :(得分:3)

MSDN:Array class

  

公共静态(在Visual Basic中共享)   这种类型的成员是线程安全的。   任何实例成员都不是   保证是线程安全的。

     

此实现不提供   synchronized(线程安全)包装器   数组;但是,.NET Framework   基于Array的类提供了它们   自己的同步版本   使用SyncRoot进行收集   属性。

这不是线程安全的!

编辑:

一些额外的信息,在正常情况下不会调用Array类上的方法SetValue,只有在通过IList接口使用数组时才调用它。

以下代码:

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

不会生成对SetValue()的调用,而是生成OpCodes.Stelem操作码。

因此,如果SetValue方法是线程安全的,那么它是无关紧要的,除非使用IList引用访问该数组。

答案 1 :(得分:0)

在每个线程在数组中设置不同项的示例中,它将是线程安全的。 看到它是一个冰箱,那里有三罐啤酒,三个不同的去冰箱,拿起他的啤酒罐,一切顺利,他们将中途饮用,放回去,以后再回来。

然而,无论是在编程还是在现实生活中,你都无法与三个人分享1罐啤酒。

所以是的:

  

如果每个线程只在数组的一个单独部分上工作,那么一切都很好

PS:我没有来源验证这一点,但碰巧一直使用线程,当每个线程读取'n同时在数组中写入时,我从未遇到过饥饿(?)问题。我在分享“同样的啤酒”时会遇到问题因此我相信我的答案是正确的,但我希望看到有人验证这一点。

答案 2 :(得分:0)

在您的示例中,对InternalSetValue(void *, object)的调用正在对三个不同的内存位置进行。因此,它应该是线程安全的。对这些位置的写入不会渗透到其他位置,即使它们是同一阵列的成员。