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