Параметры задачи, указывают ли типы ссылок на один и тот же адрес памяти в куче
Насколько я понял, когда вы используете ссылочный тип в качестве параметра в методе, значение в стеке копируется, и поэтому формальный параметр указывает на тот же адрес памяти в куче, что и оригинал, следовательно, изменяется сохраняются, как только вы закончили с методом.
Как это работает с задачами? Я только что создал 2 новые задачи и передал в массив, который был объявлен в потоке пользовательского интерфейса. Изменения, внесенные в одно из новых заданий, сразу же были показаны во втором задании. Когда я пытаюсь изменить вход (массив) через поток пользовательского интерфейса, тот же параметр не изменился в 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;
}
}
}
}
}
2 ответа
Ваш анализ кажется правильным с самого начала, но ваши выводы не так.
У вас есть ссылочный тип (массив), и вы передаете его методу по значению (по умолчанию). Это означает, что ссылка на этот массив, который находится в куче, копируется.
Потому что обе переменные в SpecialMethod
а также Main
имеют одинаковые ссылки, изменяя значение, на которое они ссылаются, будут "видны" обеими переменными.
Это применимо только если вы изменяете массив. Это то, что вы делаете с readToEnd
Вот почему разделы кода, связанные с ним, работают так, как вы и предполагали.
С data
с другой стороны, вы не изменяете массив, вы просто назначаете новый массив переменной. Это меняет ссылку, а не объект, на который она ссылается, и именно поэтому у вас возникают проблемы.
Что касается решений, то их несколько. Во-первых, вы можете изменить код, чтобы изменить массив, а не назначать новый; просто измените существующие значения. Если вам нужно изменить количество элементов, рассмотрите возможность использования List
а не массив.
Другой вариант, вместо передачи массива, это добавить еще один уровень косвенности. Вы можете создать новый класс со свойством, представляющим собой массив, передать объект этого типа SpecialMethod
, а затем вы можете изменить свойство этого объекта и увидеть его отражение в обоих местах. Вы можете использовать что-то вроде этого, чтобы охватить общий случай:
public class Wrapper<T>
{
public T Value { get; set; }
}
Вы создаете массив:
int[] data = new int[] { 1, 2, 3, 4, 5, 6 };
Затем вы передаете копию вашей ссылки в этот массив (в настоящее время хранится в data
) в качестве параметра SpecialMethod
(через захват в вашем исходном коде, но не важно, это все еще просто копия).
В SpecialMethod
параметр int[] historicalData
получит копию ссылки на этот оригинальный массив.
После этого все, что вызывает переменную data
для переназначения (в отличие от изменений, внесенных в данные в массиве, на который ссылается data
) не влияет на любые копии, которые были сделаны из его исходной ссылки - они все еще ссылаются на исходный массив.
Мне не ясно, каковы ваши реальные требования в отношении передачи данных между потоками, поэтому я не могу дать какие-либо твердые рекомендации. Я обычно стараюсь избегать использования сырых массивов.