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

发布于 2024-08-14 06:35:06 字数 2687 浏览 6 评论 0原文

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

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

正如您所看到的,每个线程在静态数组中设置一个不同的、唯一的项目。我使用 Reflector 深入研究了代码(并研究了 mscorlib.pdb)。最终有一个调用:

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

没有记录。查看 MSDN 的文档 System.Array 一般来说,以及 SetValue(object, int ) 特别是.. 没有关于线程安全的内容(或者也许我遗漏了一些东西)。

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

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

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

李白 2024-08-21 06:35:06

MSDN:数组类

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

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

它不是线程安全的!

编辑:

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

以下代码:

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

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

因此,除非使用 IList 引用访问数组,否则 SetValue 方法是否线程安全并不重要。

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.

灼疼热情 2024-08-21 06:35:06

在您的示例中,每个线程在数组中设置不同的项目,它将是线程安全的。
把它看作一个冰箱,里面有三罐啤酒,三罐不同的去冰箱挑选他的一罐啤酒,一切都会很顺利,他们会喝一半,放回去,稍后再回来。

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

所以是的:

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

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

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.

娇柔作态 2024-08-21 06:35:06

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文