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一般的に、そしてSetValue(object, int)特に..スレッドセーフについては何もありません(または私が何かを見逃しているかもしれません)MSDNのドキュメントを見てください。

同様の質問に対するJonSkeetの回答で表現されているように:

各スレッドが配列の別々の部分でのみ機能する場合、すべてがうまくいくと思います

私はこの問題に対する明確な答えを得ようGetValue(int)としています。SetValue(object, int)誰かがドキュメントリンクおよび/またはより良い理解を持っていますかInternalSetValue

4

3 に答える 3

3

MSDN:配列クラス

この型の public static (Visual Basic では共有) メンバーは、スレッド セーフです。インスタンス メンバーは、スレッド セーフであるとは限りません。

この実装は、配列の同期 (スレッド セーフ) ラッパーを提供しません。ただし、Array に基づく .NET Framework クラスは、SyncRoot プロパティを使用して独自の同期バージョンのコレクションを提供します。

スレッドセーフではありません!

編集:

追加情報として、Array クラスのメソッド SetValue は通常の状況では呼び出されません。配列が IList インターフェイスを介して使用される場合にのみ呼び出されます。

次のコード:

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

SetValue() の呼び出しは生成されず、代わりに OpCodes.Stelem オペコードが生成されます。

そのため、IList 参照を使用して配列にアクセスしない限り、SetValue メソッドがスレッド セーフかどうかは関係ありません。

于 2009-12-09T15:21:35.430 に答える
0

各スレッドが配列に異なる項目を設定する例では、スレッドセーフになります。冷蔵庫に例えると、そこにはビールの缶が 3 つあり、3 人が冷蔵庫に行き、自分の缶ビールを選びます。

ただし、プログラミングでも実生活でも、1 缶のビールを 3 人で共有することはできません。

そうそう:

各スレッドが配列の個別の部分でのみ機能する場合、すべてがうまくいきます

PS: これを検証する情報源はありませんが、たまたま常にスレッドを使用しており、各スレッドが読み取りと配列への書き込みを同時に行ったときに飢餓 (?) 問題が発生したことはありません。「同じ缶ビール」を共有するときに問題が発生するので、私の答えは正しいと思いますが、誰かにこれを確認してもらいたいです。

于 2009-12-09T15:16:53.300 に答える
0

あなたの例では、呼び出しはInternalSetValue(void *, object)3 つの異なるメモリ ロケーションに対して行われています。したがって、スレッドセーフである必要があります。これらの場所への書き込みは、同じ配列のメンバーであっても、他の場所に流出することはありません。

于 2009-12-09T15:24:08.467 に答える