我们在办公室进行了一些讨论,没有记录答案:
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
?
答案 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)
的调用正在对三个不同的内存位置进行。因此,它应该是线程安全的。对这些位置的写入不会渗透到其他位置,即使它们是同一阵列的成员。