C# 任务参数,引用类型是否指向堆上的相同内存地址

C# 任务参数,引用类型是否指向堆上的相同内存地址,c#,c#-4.0,task-parallel-library,task,reference-type,C#,C# 4.0,Task Parallel Library,Task,Reference Type,据我所知,当在方法中使用引用类型作为参数时,堆栈上的值将被复制,因此形式参数指向堆上与原始参数相同的内存地址,因此,一旦使用完该方法,更改将被持久化 这是如何处理任务的?我刚刚创建了2个新任务,并传入了一个在UI线程上声明的数组。在其中一个新任务中所做的更改将立即显示在第二个任务中。当我试图通过UI线程更改输入(数组)时,两个新任务上的相同参数没有更改。我觉得应该是这样的,因为它们应该都指向堆上的同一个内存位置 using System; using System.Collections.Gen

据我所知,当在方法中使用引用类型作为参数时,堆栈上的值将被复制,因此形式参数指向堆上与原始参数相同的内存地址,因此,一旦使用完该方法,更改将被持久化

这是如何处理任务的?我刚刚创建了2个新任务,并传入了一个在UI线程上声明的数组。在其中一个新任务中所做的更改将立即显示在第二个任务中。当我试图通过UI线程更改输入(数组)时,两个新任务上的相同参数没有更改。我觉得应该是这样的,因为它们应该都指向堆上的同一个内存位置

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;
                }
            }
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用系统线程;
命名空间任务和内存
{
班级计划
{
专用静态手动复位事件mre=新手动复位事件(假);
静态void Main(字符串[]参数)
{
int[]readToEnd=新的int[2];
int[]data=newint[]{1,2,3,4,5,6};
int valueType=5;
int pageCounter=1;
任务[]任务=新任务[2];
对于(int x=1;x<3;x++)
{
//因关闭问题而需要
int i=x;
tasks[i-1]=Task.Factory.StartNew(()=>
{
特殊方法(数据、readToEnd、i、valueType);
});
}
while(分页计数器<4)
{
if(readToEnd[0]==1&&readToEnd[1]==1)
{
//将事件的状态设置为未签名,从而导致线程阻塞
mre.Reset();
int[]temp=newint[]{7,8,9,10,11,12};
数据=温度;
readToEnd[0]=0;
readToEnd[1]=0;
//将事件的状态设置为已发出信号,允许一个或多个等待线程继续。
mre.Set();
pageCounter++;
}
}
Console.ReadLine();
}
公共静态无效特殊方法(int[]历史数据、int[]readToEnd、int taskNumber、int valueTy)
{
int[]temp=newint[]{100200300400500600};
对于(int x=0;x,创建一个数组:

int[] data = new int[] { 1, 2, 3, 4, 5, 6 };
然后将引用的副本(当前存储在
数据中)作为参数传递给
特殊方法(通过原始代码中的捕获,但并不重要,它仍然只是一个副本)

SpecialMethod
中,参数
int[]historicalData
将接收到此原始数组的引用副本

此后,任何导致变量
data
被重新分配的操作(与对
data
引用的数组中的数据所做的更改相反)都不会对其原始引用的任何副本产生影响-它们仍然引用原始数组

我不清楚您在线程间传递数据方面的实际需求是什么,因此我无法给出任何明确的建议。不过,我通常会尽量避免使用原始数组。

您创建了一个数组:

int[] data = new int[] { 1, 2, 3, 4, 5, 6 };
然后将引用的副本(当前存储在
数据中)作为参数传递给
特殊方法(通过原始代码中的捕获,但并不重要,它仍然只是一个副本)

SpecialMethod
中,参数
int[]historicalData
将接收到此原始数组的引用副本

此后,任何导致变量
data
被重新分配的操作(与对
data
引用的数组中的数据所做的更改相反)都不会对其原始引用的任何副本产生影响-它们仍然引用原始数组


我不清楚您在线程间传递数据方面的实际需求是什么,因此我无法提出任何明确的建议。不过,我通常会尽量避免使用原始数组。

您的分析一开始似乎是正确的,但您的结论却不正确

您有一个引用类型(数组),并通过值(默认值)将其传递给方法。这意味着对该数组的引用(位于堆上)被复制

由于
SpecialMethod
Main
中的变量具有相同的引用,因此更改它们引用的值将被两个变量“看到”

这仅适用于对数组进行变异的情况。这就是使用
readToEnd
所做的,这就是为什么处理它的代码部分按预期工作的原因

另一方面,使用
data
时,您不需要对数组进行变异,只需为变量指定一个新数组。这会更改引用,而不是它引用的对象,这就是您遇到问题的原因

关于解决方案,有几个。首先,您可以更改代码以更改数组而不是分配新的数组;只需更改Exchange值。如果需要更改元素的数量,请考虑使用<代码>列表< /C> >而不是数组。

另一种选择是,不传递数组,而是添加另一层间接寻址。您可以创建一个具有数组属性的新类,将该类型的对象传递给
SpecialMethod
,然后您可以更改该对象的属性,并查看它在两个位置的反映。您可以使用类似的方法来覆盖一般情况:

public class Wrapper<T>
{
    public T Value { get; set; }
}
公共类包装器
{
公共T值{get;set;}
}

一开始你的分析似乎是正确的,但你的结论却不正确

你有一个参照物