質問

As far as i had understood, when you use a reference type as a parameter in a method, the value on the stack, is copied and the formal parameter therefore points to the same memory address, on the heap, as the original, hence changes are persisted once you have finished with the method.

How does this work with tasks? I have just created 2 new tasks and passed in an array which was declared on the UI thread. Changes made in one of the new tasks were immediately shown in the second task. When i try to change the input(the array) via the UI thread, the same parameter hasnt changed on the 2 new tasks. I was under the impression it should have since they should all be pointing at the same memory location on the heap??

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;
                }
            }
        }
    }
}
役に立ちましたか?

解決 2

Your analysis seems correct at the start, but your conclusions are not.

You have a reference type (an array) and you pass it to a method by value (which is the default). This means that the reference to that array, which sits on the heap, is copied.

Because both the variable in SpecialMethod and Main have the same reference, changing the value that they reference will be "seen" by both variables.

That only applies if you mutate the array. That's what you do with readToEnd, which is why the sections of code dealing with it work as you intended.

With data on the other hand you don't mutate the array, you just assign a new array to the variable. That's changing the reference, not the object that it references, and that's why you're having problems.

As for solutions, there are several. First, you could change the code to mutate the array rather than assigning a new one; just change the exising values. If you need to change the number of elements consider using a List rather than an array.

Another option is, rather than passing an array, is to add another layer of indirection. You could make a new class that has a property which is an array, pass an object of that type to SpecialMethod, and then you can change that object's property and see it reflected in both locations. You could use something like this to cover the general case:

public class Wrapper<T>
{
    public T Value { get; set; }
}

他のヒント

You create an array:

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

You then pass a copy of your reference to this array (currently stored in data) as a parameter to SpecialMethod (via a capture in your original code, but not important, it's still just a copy).

Within SpecialMethod, the parameter int[] historicalData will receive a copy of the reference to this original array.

Thereafter, anything that causes the variable data to be reassigned (as opposed to changes made to data within the array referenced by data) has no effect on any copies that were made of its original reference - they're still referencing the original array.

I'm not clear on what your actual requirements are in terms of passing data between threads, so I cannot come up with any firm recommendations. I would usually try to avoid using raw arrays though.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top