5

我们在办公室进行了一些讨论,但没有得到书面答复:

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

没有记录。System.Array总体上看一下 MSDN 的文档SetValue(object, int),特别是.. 没有关于线程安全的内容(或者我可能遗漏了一些东西)。

正如 Jon Skeet 对类似问题的回答所表达的那样:

我相信如果每个线程只在数组的一个单独部分上工作,一切都会好起来的

我正试图就这个问题得到一个明确的GetValue(int)答案SetValue(object, int)。有人有文档链接和/或更好的理解InternalSetValue吗?

4

3 回答 3

3

MSDN:数组类

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

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

这不是线程安全的!

编辑:

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

以下代码:

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

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

因此,除非使用 IList 引用访问数组,否则 SetValue 方法是否是线程安全的,这无关紧要。

于 2009-12-09T15:21:35.430 回答
0

在您的示例中,每个线程在数组中设置不同的项目,它将是线程安全的。把它看成一个冰箱,里面有三罐啤酒,三个不同的人去冰箱里挑他那罐啤酒,一切都会好起来的,他们会喝到一半,放回去,待会再来。

但是,无论是在编程时还是在现实生活中,您都不能与三个人分享 1 罐啤酒。

是的:

如果每个线程只在数组的一个单独部分上工作,一切都会好起来的

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

于 2009-12-09T15:16:53.300 回答
0

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

于 2009-12-09T15:24:08.467 回答