0

私が理解している限り、参照型をメソッドのパラメーターとして使用すると、スタック上の値がコピーされ、仮パラメーターはヒープ上の元と同じメモリアドレスを指すため、変更されますメソッドを終了すると、永続化されます。

これはタスクでどのように機能しますか? 2 つの新しいタスクを作成し、UI スレッドで宣言された配列を渡しました。新しいタスクの 1 つで行われた変更は、すぐに 2 番目のタスクに表示されました。UI スレッドを介して入力 (配列) を変更しようとすると、2 つの新しいタスクで同じパラメーターが変更されません。それらはすべてヒープ上の同じメモリ位置を指しているはずなので、私はそれがあるべきだという印象を受けました??

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace TasksAndMemory
{
    class Program
    {
        private static ManualResetEvent mre = new ManualResetEvent(false);

        static void Main(string[] args)
        {
            int[] readToEnd = new int[2];
            int[] data = new int[] { 1, 2, 3, 4, 5, 6 };
            int valueType = 5;


            int pageCounter = 1;


            Task[] tasks = new Task[2];


            for (int x = 1; x < 3; x++)
            {
                //Needed due to closure problem
                int i = x;

                tasks[i-1] = Task.Factory.StartNew(() =>
                {
                    SpecialMethod(data, readToEnd, i, valueType);
                });
            }

            while(pageCounter < 4)
            {
                if (readToEnd[0] == 1 && readToEnd[1] == 1)
                {
                    //Sets the state of the event to nonsignaled, causing threads to block
                    mre.Reset();
                    int[] temp = new int[] { 7, 8, 9, 10, 11, 12 };
                    data = temp;
                    readToEnd[0] = 0;
                    readToEnd[1] = 0;

                    //Sets the state of the event to signaled, allowing one or more waiting threads to proceed.
                    mre.Set();
                    pageCounter++;
                }
            }
            Console.ReadLine();
        }

        public static void SpecialMethod(int[] historicalData, int[] readToEnd, int taskNumber, int valueTy)
        {
            int[] temp = new int[] { 100, 200, 300, 400, 500, 600 };

            for (int x = 0; x <= historicalData.Length; x++)
            {
                if (x == historicalData.Length)
                {
                    readToEnd[taskNumber-1] = 1;
                    mre.WaitOne();
                    x = 0;
                 }
                else
                {
                    valueTy++;
                    temp[x] = temp[x] + taskNumber;
                }
            }
        }
    }
}
4

2 に答える 2

2

配列を作成します。

int[] data = new int[] { 1, 2, 3, 4, 5, 6 };

次に、この配列への参照のコピー(現在はに格納されてます)をパラメーターとして(元のコードのキャプチャを介して)渡しますが、重要ではありませんが、それでも単なるコピーです。dataSpecialMethod

SpecialMethodで、パラメータはこの元の配列への参照のコピーint[] historicalDataを受け取ります。

その後、 (によって参照されるdata配列内のデータに加えられた変更とは対照的に)変数が再割り当てされる原因は、元の参照から作成されたコピーには影響しません。それらは引き続き元の配列を参照しています。data

スレッド間でデータを渡すという点での実際の要件が明確ではないため、確固たる推奨事項を思い付くことができません。しかし、私は通常、生の配列の使用を避けようとします。

于 2012-11-12T15:45:52.060 に答える
1

あなたの分析は最初は正しいように見えますが、あなたの結論は正しくありません。

参照型(配列)があり、それを値(デフォルト)でメソッドに渡します。これは、ヒープ上にあるその配列への参照がコピーされることを意味します。

との両方の変数が同じ参照SpecialMethodMain持っているため、それらが参照する値を変更すると、両方の変数によって「認識」されます。

これは、アレイを変更した場合にのみ適用されます。それがあなたが行うことです。それがreadToEnd、それを扱うコードのセクションが意図したとおりに機能する理由です。

data一方、配列を変更せずに、変数に新しい配列を割り当てるだけです。それが参照するオブジェクトではなく、参照を変更しているので、問題が発生しています。

解決策としては、いくつかあります。まず、新しい配列を割り当てるのではなく、コードを変更して配列を変更することができます。既存の値を変更するだけです。要素の数を変更する必要がある場合Listは、配列ではなく使用を検討してください。

別のオプションは、配列を渡すのではなく、間接参照の別のレイヤーを追加することです。配列であるプロパティを持つ新しいクラスを作成し、そのタイプのオブジェクトをに渡すSpecialMethodことができます。次に、そのオブジェクトのプロパティを変更して、両方の場所に反映されることを確認できます。次のようなものを使用して、一般的なケースをカバーできます。

public class Wrapper<T>
{
    public T Value { get; set; }
}
于 2012-11-12T15:46:05.310 に答える